Actions property on route object
Route actions are defined on the `action` property of a route object. Actions handle data mutations. When an action completes, all loader data on the page is automatically revalidated to keep the UI in sync with data.
Action parameter: request
Actions receive a request object as a parameter. You can extract form data from the request using `await request.formData()` to get a FormData object, then retrieve individual fields using `.get(fieldName)`.
Full action definition example
This example shows a complete action definition: `{ path: "/projects/:projectId", Component: Project, action: async ({ request }) => { let formData = await request.formData(); let title = formData.get("title"); let project = await someApi.updateProject({ title }); return project; } }`
Data mutations done through Route actions
Data mutations in React Router are performed through Route actions. When an action completes, all loader data on the page is automatically revalidated to keep the UI in sync with the data without requiring manual code to handle synchronization.
Server actions vs client actions definition
Route actions defined with the 'action' export only run on the server and are removed from client bundles. Route actions defined with 'clientAction' export run in the browser. Client actions take priority over server actions when both are defined on the same route.
Server action function signature and example
A server action function receives Route.ActionArgs containing a request object. The function is async and retrieves formData from request.formData(), then processes it and returns data. Example: export async function action({ request }: Route.ActionArgs) { let formData = await request.formData(); let title = formData.get('title'); let project = await fakeDb.updateProject({ title }); return project; }
Access action data in component
Action data returned from an action is accessible in the route component through Route.ComponentProps. The component receives actionData which contains the data returned by the action and can be undefined if no action has been called yet.
useFetcher for independent pending state
The useFetcher hook provides independent pending state for form submissions without causing global navigation. It has a state property that can be checked against 'idle' to determine if a submission is in progress. useFetcher also provides a Form component for submitting data.
useFetcher form submission pending state
Use fetcher.state to check form submission status. When fetcher.state is not 'idle', the form is submitting. Display appropriate UI feedback such as 'Submitting...' text on the submit button.
useNavigation for non-fetcher form pending state
For form submissions using Form component instead of fetcher.Form, use useNavigation() to check pending state. The navigation.formAction property contains the action URL of the submitted form. Compare this to the expected action to determine if that specific form is submitting.
Optimistic UI with fetcher.formData
Implement optimistic UI by checking fetcher.formData during form submission. The formData contains the submitted form fields. Use formData.get(fieldName) to read submitted values and update the UI optimistically before the server responds.
redirect helper for returning navigation Response
Use `redirect(url)` from react-router to return a Response that tells the app to change locations. Example: `return redirect('/contacts/123')`. With client-side routing, this is a client-side redirect so users don't lose scroll positions or component state. Works like a normal server redirect without JavaScript.
Form component for data mutations
Use `<Form>` from react-router instead of HTML `<form>` for data mutations. Forms prevent browser default behavior, serialize form data, and send it to the route's `action` function via fetch instead of to the server. This emulates HTML form navigation with client-side routing.
action function for handling form submissions
Export an async `action()` function from a route module to handle form submissions. Receives a request with serialized form data. Usage: `export async function action({ params, request }: Route.ActionArgs) { const formData = await request.formData(); const updates = Object.fromEntries(formData); await updateContact(params.contactId, updates); return redirect(...); }` After action completes, React Router automatically revalidates loaders.
Relative Form action attribute
Like `<Link to>`, `<Form action>` accepts relative values. When in route contacts/:contactId, `<Form action='destroy'>` submits to contacts/:contactId/destroy. This allows modular route actions without hardcoding full paths.
GET forms for URL parameter changes
`<Form>` without `method='post'` (or with `method='get'`) serializes form data as URLSearchParams in the URL query string instead of the request body. No action function is called; only loaders run (normal page navigation). Useful for search/filter forms that don't mutate data.
POST form automatic replace behavior
When a POST form submission does not redirect, the router uses REPLACE in the history stack to avoid duplicate entries. When a POST form submission does redirect, the router uses PUSH for the redirect to preserve the history entry for the login page. Users can override this with the replace prop: <Form method="post" replace={shouldReplace}>.