UNPKG

3.49 kB Markdown View Raw
1---
2title: Form Validation
3---
4
5# Form Validation
6
7[MODES: framework, data]
8
9This guide walks through a simple signup form implementation. You will likely want to pair these concepts with third-party validation libraries and error components, but this guide only focuses on the moving pieces for React Router.
10
11## 1. Setting Up
12
13We'll start by creating a basic signup route with form.
14
15```ts filename=app/routes.ts
16import {
17 type RouteConfig,
18 route,
19} from "@react-router/dev/routes";
20
21export default [
22 route("signup", "signup.tsx"),
23] satisfies RouteConfig;
24```
25
26```tsx filename=signup.tsx
27import type { Route } from "./+types/signup";
28import { useFetcher } from "react-router";
29
30export default function Signup(_: Route.ComponentProps) {
31 let fetcher = useFetcher();
32 return (
33 <fetcher.Form method="post">
34 <p>
35 <input type="email" name="email" />
36 </p>
37
38 <p>
39 <input type="password" name="password" />
40 </p>
41
42 <button type="submit">Sign Up</button>
43 </fetcher.Form>
44 );
45}
46```
47
48## 2. Defining the Action
49
50In this step, we'll define a server `action` in the same file as our `Signup` component. Note that the aim here is to provide a broad overview of the mechanics involved rather than digging deep into form validation rules or error object structures. We'll use rudimentary checks for the email and password to demonstrate the core concepts.
51
52```tsx filename=signup.tsx
53import type { Route } from "./+types/signup";
54import { redirect, useFetcher, data } from "react-router";
55
56export default function Signup(_: Route.ComponentProps) {
57 // omitted for brevity
58}
59
60export async function action({
61 request,
62}: Route.ActionArgs) {
63 const formData = await request.formData();
64 const email = String(formData.get("email"));
65 const password = String(formData.get("password"));
66
67 const errors = {};
68
69 if (!email.includes("@")) {
70 errors.email = "Invalid email address";
71 }
72
73 if (password.length < 12) {
74 errors.password =
75 "Password should be at least 12 characters";
76 }
77
78 if (Object.keys(errors).length > 0) {
79 return data({ errors }, { status: 400 });
80 }
81
82 // Redirect to dashboard if validation is successful
83 return redirect("/dashboard");
84}
85```
86
87If any validation errors are found, they are returned from the `action` to the fetcher. This is our way of signaling to the UI that something needs to be corrected, otherwise the user will be redirected to the dashboard.
88
89Note the `data({ errors }, { status: 400 })` call. Setting a 400 status is the web standard way to signal to the client that there was a validation error (Bad Request). In React Router, only 2xx status codes trigger page data revalidation, so sending a 400 status prevents the normal revalidation that would occur after an `action`.
90
91## 3. Displaying Validation Errors
92
93Finally, we'll modify the `Signup` component to display validation errors, if any, from `fetcher.data`.
94
95```tsx filename=signup.tsx lines=[3,8,13-15]
96export default function Signup(_: Route.ComponentProps) {
97 let fetcher = useFetcher();
98 let errors = fetcher.data?.errors;
99 return (
100 <fetcher.Form method="post">
101 <p>
102 <input type="email" name="email" />
103 {errors?.email ? <em>{errors.email}</em> : null}
104 </p>
105
106 <p>
107 <input type="password" name="password" />
108 {errors?.password ? (
109 <em>{errors.password}</em>
110 ) : null}
111 </p>
112
113 <button type="submit">Sign Up</button>
114 </fetcher.Form>
115 );
116}
117```