Check out our talk at Remix Conf!


In this example, all sorts of string schemas are validated on the client and on the server.

const schema = z.object({
  nonEmpty: z.string().min(1),
  optional: z.string().optional(),
  nullable: z.string().nullable(),
  default: z.string().default('Foo Bar'),
  minLength: z.string().min(5),
  maxLength: z.string().max(10),
  email: z.string().email(),
  url: z.string().url(),
  phoneNumber: z
      'Invalid phone number',

const mutation = makeDomainFunction(schema)(async (values) => values)

export const action: ActionFunction = async ({ request }) =>
  formAction({ request, schema, mutation })

export default () => <Form schema={schema} />