React Zorm
Type safe <form>
for React using Zod!
Tools for creating type safe forms using the browser native <form>
and
FormData
with React.js and Zod.
Features / opinions
- No controlled inputs
- No React components, just a React hook
- Validation on the client and the server
- When your server supports
FormData
like Remix!
- When your server supports
- Nested / array / object fields
- Tiny: Less than 3kb (minified & gzipped, not including Zod)
- Type safe
name
andid
attribute generation- Error referencing
Install
npm install react-zorm
Example
Also on Codesandbox!
import { z } from "zod";
import { createValidator } from "react-zorm";
const FormValues = z.object({
email: z.string().min(1),
password: z.string().min(8),
});
const { useValidation, fields } = createValidator("signup", FormValues);
function Signup() {
const { validation, props, errors } = useValidation();
return (
<form
{...props({
// custom form props
onSubmit(e) {},
})}
>
Email:
<input
type="text"
// Generate name attribute by calling the method
name={fields.email()}
// Add "errored" class when the field has a validation error
className={errors.email("errored")}
/>
{errors.email((e) => (
// Rendered when the field has an error
<ErrorMessage message={e.message} />
))}
Password:
<input
type="password"
name={fields.password()}
className={errors.password("errored")}
/>
{errors.password((e) => (
<ErrorMessage message={e.message} />
))}
<button type="submit">Signup!</button>
</form>
);
}
Also checkout this classic TODOs example demonstrating almost every feature in the library.
Objects
Create a Zod type with a nested object
const FormValues = z.object({
user: z.object({
email: z.string().min(1),
password: z.string().min(8),
}),
});
and just create the input names with .user.
:
<input type="text" name={fields.user.email()} />;
<input type="password" name={fields.user.password()} />;
And all this is type checked ?
Arrays
Array of user objects for example:
const FormValues = z.object({
users: z.array(
z.object({
email: z.string().min(1),
password: z.string().min(8),
}),
),
});
and put the array index to users(index)
:
users.map((user, index) => {
return (
<>
<input type="text" name={fields.users(index).email()} />
<input type="password" name={fields.users(index).password()} />
</>
);
});
See the TODOs example for more deatails
Server-side validation
This is Remix but React Zorm does not actually use any Remix APIs so this method
can be adapted for example to Cloudflare Workers and any other tools using the
web platform APIs.
import { parseForm } from "react-zorm";
export let action: ActionFunction = async ({ request }) => {
const form = await request.formData();
// Get validated and typed form object. This throw on validation errors.
const data = parseForm(FormValues, form);
};
API
createValidator(formName: string, formParser: ZodObject): Validator
Create a form Validator
Validator
properties
fields
: Chainable object for generating inputname
s andid
s- Call without arguments or with
.prop("name")
to generate the input name attribute value - Call
.prop("id")
to generate a unique HTML id. Use foraria-describedby
for example
- Call without arguments or with
useValidation(): ValidationObject
: React hook for using the validator with a<form>
useValidationContext(): ValidationContextObject
: React hook for using the validator from nested components
ValidationObject
properties
props(customize: HTMLFormElement): HTMLFormElementProperties
: Get spreadable props for<form>
validation
: The current Zod validation status returned bysafeParse()
- The validation started on the first submit and after that on every input blur event
validate(): void
: Manually invoke validationContext
: Context React component for providing the value foruseValidationContext()