Remix Validated Form

v6 is coming!

Check out the RFC to get an early look or leave a comment.

Server validation

One of the core features of this library is that you can re-use your validation on the server. In the last example, we created this validator to use in our form.

export const validator = withZod(
    firstName: z
      .min(1, { message: "First name is required" }),
    lastName: z
      .min(1, { message: "Last name is required" }),
    email: z
      .min(1, { message: "Email is required" })
      .email("Must be a valid email"),

We can re-use this validator in our action like this.

export const action = async ({
}: DataFunctionArgs) => {
  const result = await validator.validate(
    await request.formData()

  if (result.error) {
    // validationError comes from `remix-validated-form`
    return validationError(result.error);

  const { firstName, lastName, email } =;
  // Do something with the data

When we use the validationError to return our error, the ValidatedForm in our route component will automatically display the errors on the form fields. We pass the original data the user submitted as a second argument to validationError to specify how to repopulate the form if the user has JavaScript disabled. You can read more about this in the API reference for validationError.

Full implementation

If we put everything together, we have a fully working example.