react-zod-form v0.4.1
React Zod Form
Simple form handling with full validation control.
Navigation
Other approaches
Read this article to know how to choose your form library.
Install
npm
npm i react-zod-formFeatures
- Zod
- Field value parser by apparent rules
- Field names helper
Usage
Getting values
If you're not familiar with Zod, begin with it first.
You start from creating new ZodForm
import ZodForm from "react-zod-form"
const form = new ZodForm()Then you declare some fields
import ZodForm from "react-zod-form"
import { z } from "zod"
const form = new ZodForm({
userName: z.string().min(3, "Enter at least 3 chars"),
email: z.string().email("Follow this email format: email@example.com"),
website: z.string().url("Follow this URL format: https://example.com")
})You just created Zod form!
Notice: It's better to keep zod form in the low level to make sure you're creating ZodForm only once.
Now let's create react form component
function ExampleForm() {
return (
<form>
<input placeholder="Enter your username" required />
<input placeholder="Enter your email" type="email" required />
<input placeholder="Enter your website" type="url" required />
</form>
)
}
export default ExampleFormCombine zod schema and give fields their names (help yourself with fields)
import ZodForm from "react-zod-form"
import { z } from "zod"
const form = new ZodForm({
userName: z.string().min(3, "Enter at least 3 chars"),
email: z.string().email("Follow this email format: email@example.com"),
website: z.string().url("Follow this URL format: https://example.com")
})
function ExampleForm() {
return (
<form>
<input placeholder="Enter your username" required name={form.fields.username} />
<input placeholder="Enter your email" type="email" required name={form.fields.email} />
<input placeholder="Enter your website" type="url" required name={form.fields.url} />
</form>
)
}
export default ExampleFormNow let's get some values on event (i.e. onBlur, onFocus, onChange, onSubmit, ...)
import { FormEvent } from "react"
import ZodForm from "react-zod-form"
import { z } from "zod"
const form = new ZodForm({
userName: z.string().min(3, "Enter at least 3 chars"),
email: z.string().email("Follow this email format: email@example.com"),
website: z.string().url("Follow this URL format: https://example.com")
})
function ExampleForm() {
/**
* Triggered on input unfocus.
*/
function onBlur(event: FormEvent<HTMLFormElement>) {
event.preventDefault()
// Tries to return a field that was currently unfocused, otherwise throws error
const field = form.parseCurrentField(event)
console.log(field.name, field.value)
// Tries to return all field values, otherwise throws error
const fields = form.parseAllFields(event)
console.log(fields)
}
return (
<form onBlur={onBlur}>
<input placeholder="Enter your username" required name={form.fields.username} />
<input placeholder="Enter your email" type="email" required name={form.fields.email} />
<input placeholder="Enter your website" type="url" required name={form.fields.url} />
</form>
)
}
export default ExampleFormWow, now you have your fields just in a few lines of code and it's all concise!
Notice: There is a safe version of parseAllFields - safeParseAllFields, works just same as in zod.
Let's talk about form interface as you may want your form to be a "standonle module"
import { FormEvent } from "react"
import ZodForm from "react-zod-form"
import { z } from "zod"
const form = new ZodForm({
userName: z.string().min(3, "Enter at least 3 chars"),
email: z.string().email("Follow this email format: email@example.com"),
website: z.string().url("Follow this URL format: https://example.com")
})
export type ExampleFormFields = z.infer<typeof form.object>
interface ExampleFormProps {
onBlur?(value: ExampleFormFields): void
}
function ExampleForm(props: ExampleFormProps) {
/**
* Triggered on input unfocus.
*/
function onBlur(event: FormEvent<HTMLFormElement>) {
event.preventDefault()
// Tries to return a field that was currently unfocused, otherwise throws error
const field = form.parseCurrentField(event)
console.log(field.name, field.value)
// Tries to return all field values, otherwise throws error
const fields = form.parseAllFields(event)
console.log(fields)
}
return (
<form onBlur={onBlur}>
<input placeholder="Enter your username" required name={form.fields.username} />
<input placeholder="Enter your email" type="email" required name={form.fields.email} />
<input placeholder="Enter your website" type="url" required name={form.fields.url} />
</form>
)
}
export default ExampleFormI'll explain you this a bit:
Interface ExampleFormFields represents a value that your output will give, your output is to be methods like onBlur, onFocus, onChange, onSubmit.
Handling issues
Issues are form errors. This is a separate module, so you need to import this along with ZodForm.
Let's take the previous example and start with reporting and clearing error
import { FormEvent } from "react"
import ZodForm, { useZodFormIssues } from "react-zod-form"
import { z } from "zod"
const form = new ZodForm({
userName: z.string().min(3, "Enter at least 3 chars"),
email: z.string().email("Follow this email format: email@example.com"),
website: z.string().url("Follow this URL format: https://example.com")
})
export type ExampleFormFields = z.infer<typeof form.object>
interface ExampleFormProps {
onBlur?(value: ExampleFormFields): void
}
function ExampleForm(props: ExampleFormProps) {
const { reportError, clearError } = useZodFormIssues(form)
/**
* Triggered on input unfocus.
*/
function onBlur(event: FormEvent<HTMLFormElement>) {
event.preventDefault()
// Tries to return all field values, otherwise throws error
const fields = form.parseAllFields(event)
if (fields.success) {
clearError() // You better clear error right after success check
console.log(fields)
} else {
reportError(fields.error)
}
}
return (
<form onBlur={onBlur}>
<input placeholder="Enter your username" required name={form.fields.username} />
<input placeholder="Enter your email" type="email" required name={form.fields.email} />
<input placeholder="Enter your website" type="url" required name={form.fields.url} />
</form>
)
}
export default ExampleFormNow continue with displaying issue message
import { FormEvent } from "react"
import ZodForm, { useZodFormIssues } from "react-zod-form"
import { z } from "zod"
const form = new ZodForm({
userName: z.string().min(3, "Enter at least 3 chars"),
email: z.string().email("Follow this email format: email@example.com"),
website: z.string().url("Follow this URL format: https://example.com")
})
export type ExampleFormFields = z.infer<typeof form.object>
interface ExampleFormProps {
onBlur?(value: ExampleFormFields): void
}
function ExampleForm(props: ExampleFormProps) {
const { reportError, clearError, fieldIssues } = useZodFormIssues(form)
/**
* Triggered on input unfocus.
*/
function onBlur(event: FormEvent<HTMLFormElement>) {
event.preventDefault()
// Tries to return all field values, otherwise throws error
const fields = form.parseAllFields(event)
if (fields.success) {
clearError() // You better clear error right after success check
console.log(fields)
} else {
reportError(fields.error)
}
}
return (
<form onBlur={onBlur}>
{fieldIssues.username}
<input placeholder="Enter your username" required name={form.fields.username} />
{fieldIssues.email}
<input placeholder="Enter your email" type="email" required name={form.fields.email} />
{fieldIssues.url}
<input placeholder="Enter your website" type="url" required name={form.fields.url} />
</form>
)
}
export default ExampleFormNotice: fieldIssues will have the same keys as your form fields.
Let's say all your form fields are valid, but something went wrong on a backend
import { FormEvent } from "react"
import ZodForm, { useZodFormIssues } from "react-zod-form"
import { z } from "zod"
const form = new ZodForm({
userName: z.string().min(3, "Enter at least 3 chars"),
email: z.string().email("Follow this email format: email@example.com"),
website: z.string().url("Follow this URL format: https://example.com")
})
export type ExampleFormFields = z.infer<typeof form.object>
interface ExampleFormProps {
onBlur?(value: ExampleFormFields): void
}
function ExampleForm(props: ExampleFormProps) {
const { reportError, clearError, fieldIssues, addIssue } = useZodFormIssues(form)
/**
* Triggered on input unfocus.
*/
function onBlur(event: FormEvent<HTMLFormElement>) {
event.preventDefault()
// Tries to return all field values, otherwise throws error
const fields = form.parseAllFields(event)
if (fields.success) {
clearError() // You better clear error right after success check
requestBackend(fields.data)
} else {
reportError(fields.error)
}
}
/**
* --- THIS IS EXAMPLE ---
*/
function requestBackend(fields: ExampleFormFields) {
const response = send(fields)
if (!response.error) return
response.error.fields.forEach(field => {
addIssue([field.name], field.message)
})
}
return (
<form onBlur={onBlur}>
{fieldIssues.username}
<input placeholder="Enter your username" required name={form.fields.username} />
{fieldIssues.email}
<input placeholder="Enter your email" type="email" required name={form.fields.email} />
{fieldIssues.url}
<input placeholder="Enter your website" type="url" required name={form.fields.url} />
</form>
)
}
export default ExampleFormField value types
A form field value may be one of these type (can be in array):
stringnumberbooleanFile
Take a look at the interface to understand it better
type FormFieldValue = FormFieldValueBasic | FormFieldValueBasic[]
type FormFieldValueBasic = string | number | boolean | FileTransform rules
| Type | Output | Description |
|---|---|---|
any | number | If value is a parsable number, it will be converted to number. |
any | boolean | If value is "true" or "false", it will be converted to boolean. |
"radio" | "checkbox" | boolean | If value is "ok" and type is "radio" \| "checkbox", the value from checked attribute will be taken. |
"file" | File \| File[] | If type is "file", it will give File or File[], depending on multiple attribute. |
1 year ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
3 years ago
3 years ago
3 years ago