new·The score now tells you which way it movedA brain's exam only ever grows: its own material writes questions, and so does every question a real caller asked and did not get answered. The score is a percentage over that growing set, so a brain that learned more could post a smaller number — and this week three did. One of them answered two MORE questions than the week before and showed eighteen points less. Printed as a single percentage, that reads as decline to a reader and as punishment to anyone who contributes material.all news →
mozg.beta
Sign in

React Router · API · all subjects

form

14 notes, read out of this brain and free to use. Each one was extracted from a source and is re-checked against its exam.

Form component overview

Form is a progressively enhanced HTML form element that submits data to actions via fetch. It activates pending states in useNavigation, enabling advanced user interfaces. After the form action completes, all data on the page is automatically revalidated to keep the UI in sync. Server rendered pages are interactive at a basic level before JavaScript loads, with the browser managing submission and pending states. After JavaScript loads, React Router takes over for web application experiences. Form is most useful for submissions that change the URL or add an entry to browser history. For forms that should not manipulate browser history, use fetcher.Form instead.

Form basic example

Example of a Form component with action and method props: ```tsx import { Form } from "react-router"; function NewEvent() { return ( <Form action="/events" method="post"> <input name="title" type="text" /> <input name="description" type="text" /> </Form> ); } ```

Form available in framework and data modes

Form is available in framework mode and data mode.

Form and fetcher API comparison

Navigation/URL APIs and Fetcher APIs provide similar functionality: <Form> corresponds to <fetcher.Form>, actionData (component prop) corresponds to fetcher.data, navigation.state corresponds to fetcher.state, navigation.formAction corresponds to fetcher.formAction, and navigation.formData corresponds to fetcher.formData.

Creating a new record example with Form and useNavigation

Example showing how to create a new record using <Form>, component props with actionData, and useNavigation. The action validates form data, creates the record in the database, and redirects to the new record's page. The component uses actionData to display validation errors and useNavigation to determine submission state for UI feedback. ```tsx import { Form, redirect, useNavigation, } from "react-router"; import type { Route } from "./+types/new-recipe"; export async function action({ request, }: Route.ActionArgs) { const formData = await request.formData(); const errors = await validateRecipeFormData(formData); if (errors) { return { errors }; } const recipe = await db.recipes.create(formData); return redirect(`/recipes/${recipe.id}`); } export function NewRecipe({ actionData, }: Route.ComponentProps) { const { errors } = actionData || {}; const navigation = useNavigation(); const isSubmitting = navigation.formAction === "/recipes/new"; return ( <Form method="post"> <label> Title: <input name="title" /> {errors?.title ? <span>{errors.title}</span> : null} </label> <label> Ingredients: <textarea name="ingredients" /> {errors?.ingredients ? ( <span>{errors.ingredients}</span> ) : null} </label> <label> Directions: <textarea name="directions" /> {errors?.directions ? ( <span>{errors.directions}</span> ) : null} </label> <button type="submit"> {isSubmitting ? "Saving..." : "Create Recipe"} </button> </Form> ); } ```

Deleting a record from a list with useFetcher

Example showing how to delete a record from a list using useFetcher without changing the URL or navigating away. The action deletes the recipe from the database and returns {ok: true}. The RecipeListItem component uses useFetcher to submit a form with the recipe id and manages the isDeleting state to provide UI feedback. Each fetcher independently manages its own state. ```tsx import { useFetcher } from "react-router"; import type { Recipe } from "./recipe.server"; import type { Route } from "./+types/recipes"; export async function action({ request, }: Route.ActionArgs) { const formData = await request.formData(); const id = formData.get("id"); await db.recipes.delete(id); return { ok: true }; } function RecipeListItem({ recipe }: { recipe: Recipe }) { const fetcher = useFetcher(); const isDeleting = fetcher.state !== "idle"; return ( <li> <h2>{recipe.title}</h2> <fetcher.Form method="post"> <input type="hidden" name="id" value={recipe.id} /> <button disabled={isDeleting} type="submit"> {isDeleting ? "Deleting..." : "Delete"} </button> </fetcher.Form> </li> ); } ```

Mark article as read with useFetcher

Example of using useFetcher.submit() to mark an article as read after the user has spent time on the page and scrolled to the bottom, without changing the URL or navigating away. ```tsx import { useFetcher } from "react-router"; function useMarkAsRead({ articleId, userId }) { const marker = useFetcher(); useSpentSomeTimeHereAndScrolledToTheBottom(() => { marker.submit( { userId }, { action: `/article/${articleId}/mark-as-read`, method: "post", }, ); }); } ```

User avatar details popup with useFetcher.load()

Example showing how to use useFetcher to load data on demand for a popup component. The UserAvatar component uses useFetcher with a loader to fetch user details on hover and display them in a popup, without navigating away or changing the URL. ```tsx import { useState, useEffect } from "react"; import { useFetcher } from "react-router"; import type { Route } from "./+types/user-details"; export async function loader({ params }: Route.LoaderArgs) { return await fakeDb.user.find({ where: { id: params.id }, }); } type LoaderData = Route.ComponentProps["loaderData"]; function UserAvatar({ partialUser }) { const userDetails = useFetcher<LoaderData>(); const [showDetails, setShowDetails] = useState(false); useEffect(() => { if ( showDetails && userDetails.state === "idle" && !userDetails.data ) { userDetails.load(`/user-details/${partialUser.id}`); } }, [showDetails, userDetails, partialUser.id]); return ( <div onMouseEnter={() => setShowDetails(true)} onMouseLeave={() => setShowDetails(false)} > <img src={partialUser.profileImageUrl} /> {showDetails ? ( userDetails.state === "idle" && userDetails.data ? ( <UserPopup user={userDetails.data} /> ) : ( <UserPopupLoading /> ) ) : null} </div> ); } ```

fetcher.Form method prop

The Form component returned by useFetcher accepts a method prop. When method="post" is specified, the form will submit as a POST request to the route's action function.

Form validation pattern with fetcher

A common pattern for form validation in React Router is: (1) render a form using fetcher.Form, (2) define an action that validates the submitted data, (3) if validation fails, return the errors object with a 400 status using data(), (4) access the errors through fetcher.data in the component and display them conditionally.

action export function

Route actions allow server-side data mutations with automatic revalidation of all loader data on the page when called from Form, useFetcher, and useSubmit. Actions are called with a request object and can return data that is passed to the component via actionData prop.

clientAction export function

clientAction is like route actions but only called in the browser. It receives a serverAction argument and can call the server action if needed, or perform client-side mutations.

Route module example with loader and action

Example showing a route module with loader that fetches items, an action that adds items, and a component that displays the items and includes a Form for creating new todos. The action automatically triggers revalidation of the loader.

clientAction example

Example showing clientAction that invalidates client-side cache and optionally calls the server action to get data.

Give your agent this brain