How toValidate Form in Remix with clientAction
Imagine you're building a form like this one
<form method="post">
<input name="title" />
<input name="slug" />
<textarea name="excerpt" />
<textarea name="content" />
</form>
Server-side Validations
Now you want to validate it, the first step would be to validate server-side, since we can't trust the client we should always start here.
routes/something.tsx export async function action({ request }: ActionFunctionArgs) { let formData = await request.formData() let body = validateFormData(formData) await doSomething(body) return { status: "success" as const } }
Here validateFormData
could use Zod to validate the title, slug, excerpt and content, the result is a correctly body with what doSomething
will expect and with everything validated.
HTML Validations
The next step would be to add client-side navigations, here the simplest way is to leverage HTML5 to do the validations, e.g.
<form method="post">
<input name="title" required />
<input name="slug" required />
<textarea name="excerpt" />
<textarea name="content" required />
</form>
Client-side Validations
And while that will obviously work it has a drawback that it's impossible to control the error UI, and the validations are not that advanced. We could use the Constraints API but it will need extra work.
Usually at this point most apps will include a form library like Conform, remix-hook-forms, react-hook-form and more.
But you don't really need them, specially if you will only use them to run a Zod schema against your form and return the errors, so you can do it yourself.
Instead of just exporting an action
we could export a clientAction
, this function will be called before your action
and it can even prevent running the server action, and of course we can do validations here the same way we do on action
.
routes/something.tsx export async function clientAction({ request, serverAction }: ClientActionFunctionArgs) { let formData = await request.formData() validateFormData(formData) return await serverAction<typeof action>() }
With this short code we parsed the request on the browser, got the FormData, and validate it.
Any validation error could be thrown and the result of the server action will be returned from the clientAction.
Now combine Zod errors with something like React Aria Components' Form validations and you can show the errors on the UI with a better design.
With all of this could build a progressively enhanced form that starts simple with a normal form and server-side validations, it can improve by adding HTML validations, and then after JS loaded use clientAction
to validate client-side and give feedback quickly as you don't need a server roundtrip.
Every validation layer is build on top of the previous one.