React Router adapter
This was previously the Remix adapter. Since Remix v3 has merged with React Router v7, we now only have a React Router adapter.
Server-side validation
One of the core features of this library is that you can re-use your validation on the server. Let's use this validator as an example:
const validator = withZod(
z.object({
firstName: z
.string()
.min(1, { message: "First name is required" }),
lastName: z
.string()
.min(1, { message: "Last name is required" }),
email: z
.string()
.min(1, { message: "Email is required" })
.email("Must be a valid email"),
}),
);
All you need to do is call validator.validate
on the form data returned from request.formData
.
export const action = async ({
request,
}: DataFunctionArgs) => {
const result = await validator.validate(
await request.formData(),
);
if (result.error) {
// validationError comes from `@rvf/react-router`
return validationError(
result.error,
result.submittedData,
);
}
const { firstName, lastName, email } = result.data;
// Do something with the data
};
When we use the validationError
to return our validation errors,
the useForm
or ValidatedForm
in our route component will automatically pick up those errors.
If you also pass result.submittedData
to as the second argument to validationError
,
it will automatically repopulate the form with the data submitted by the user.
Caveats
If you have more than one form active on the page, you'll need to take some extra steps to ensure you don't show errors on the wrong form.
const form = useForm({
validator,
id: "form-1",
})
return (
<form {...form.getFormProps()}>
{/* You don't need this part if you're using
`ValidatedForm` instead of `useForm` */}
{form.renderFormIdInput()}
{/* The rest of the form */}
</form>
)
Example
Try disabling JavaScript in your browser to see how server validation errors are handled.
Server validation with React Router
API differences
The API of useForm
and ValidatedForm
are almost identical to the base @rvf/react
API.
The main difference is that useForm
can accept many of the props you would normally pass
to Form
or useSubmit
from Remix
. In order for these to work correctly,
you should pass them to the useForm
hook instead of the Form
component.
form
or Form
?
Normally, with Remix, you would use the Form
component for most of your forms.
When using RVF though, you don't need to do that.
useForm
calls preventDefault
on the form's submit callback, to take control
of the form submission.