How to Handle Form Errors in Remix: Simple Fixes and Best Practices
action function and reading it in your component using useActionData(). This lets you show validation messages or other errors directly on the form after submission.Why This Happens
When you submit a form in Remix, if you don't return error information from your action function, the form will not show any error messages. This happens because Remix expects you to send back data that your component can use to display errors. Without this, users won't know what went wrong.
export const action = async ({ request }) => { const formData = await request.formData(); const email = formData.get('email'); if (!email) { // Missing return of error data return { errors: { email: 'Email is required' } }; } // Process form normally return null; }; export default function ContactForm() { // No useActionData used to get errors return ( <form method="post"> <input type="email" name="email" placeholder="Email" /> <button type="submit">Send</button> </form> ); }
The Fix
Return an object with error messages from the action function when validation fails. Then use useActionData() in your component to read these errors and display them near the form fields. This way, users see exactly what needs fixing.
import { useActionData } from '@remix-run/react'; export const action = async ({ request }) => { const formData = await request.formData(); const email = formData.get('email'); if (!email) { return { errors: { email: 'Email is required' } }; } // Process form normally return null; }; export default function ContactForm() { const actionData = useActionData(); return ( <form method="post" noValidate> <input type="email" name="email" placeholder="Email" aria-invalid={actionData?.errors?.email ? true : undefined} aria-describedby={actionData?.errors?.email ? 'email-error' : undefined} /> {actionData?.errors?.email && ( <p id="email-error" style={{ color: 'red' }} role="alert"> {actionData.errors.email} </p> )} <button type="submit">Send</button> </form> ); }
Prevention
Always validate form data in your action function and return clear error objects. Use useActionData() to display these errors in your form. Add aria-invalid and aria-describedby attributes for accessibility. Use noValidate on the form to disable browser default validation if you want full control.
Consider using schema validation libraries like Zod or Yup to keep validation consistent and clean. Also, test your forms by submitting invalid data to confirm errors show correctly.
Related Errors
Common related errors include:
- Not using
useActionData()to read errors, so messages never appear. - Returning plain strings instead of objects, causing rendering issues.
- Forgetting to add accessibility attributes, making errors hard to notice for screen readers.
- Relying only on client-side validation, which can be bypassed.
Fix these by following Remix patterns for server-side validation and error display.