From 7b4cff566eacda6f4825897b9f2278ebaa7704bf Mon Sep 17 00:00:00 2001 From: Sai Asish Y Date: Thu, 7 May 2026 07:55:35 -0700 Subject: [PATCH 1/3] docs(headers): document how to read request headers from loaders and actions (#14981) --- contributors.yml | 1 + docs/how-to/headers.md | 34 ++++++++++++++++++++++++++-------- 2 files changed, 27 insertions(+), 8 deletions(-) diff --git a/contributors.yml b/contributors.yml index 01178019f1..aac5220763 100644 --- a/contributors.yml +++ b/contributors.yml @@ -378,6 +378,7 @@ - ryanhiebert - saengmotmi - SailorStat +- SAY-5 - samimsu - sanjai451 - sanketshah19 diff --git a/docs/how-to/headers.md b/docs/how-to/headers.md index 5b5ee9ad48..c04c144080 100644 --- a/docs/how-to/headers.md +++ b/docs/how-to/headers.md @@ -9,9 +9,27 @@ title: HTTP Headers

+## Reading request headers + +The `request` sent to route handlers is a standard Web Fetch [Request](https://developer.mozilla.org/en-US/docs/Web/API/Request), so you can read headers directly from the [`request.headers`](https://developer.mozilla.org/en-US/docs/Web/API/Request/headers) property: + +```tsx filename=some-route.tsx +export async function loader({ + request, +}: Route.LoaderArgs) { + // Standard Headers methods are available + const userAgent = request.headers.get("User-Agent"); + const hasCookies = request.headers.has("Cookie"); + + // ... +} +``` + +## Setting response headers + Headers are primarily defined with the route module `headers` export. You can also set headers in `entry.server.tsx`. -## From Route Modules +### From Route Modules ```tsx filename=some-route.tsx import { Route } from "./+types/some-route"; @@ -28,11 +46,11 @@ export function headers(_: Route.HeadersArgs) { You can return either a [`Headers`](https://developer.mozilla.org/en-US/docs/Web/API/Headers) instance or `HeadersInit`. -## From loaders and actions +### From loaders and actions When the header is dependent on loader data, loaders and actions can also set headers. -### 1. Wrap your return value in `data` +**1. Wrap your return value in `data`** ```tsx lines=[1,8] import { data } from "react-router"; @@ -50,7 +68,7 @@ export async function loader({ params }: LoaderArgs) { } ``` -### 2. Return from `headers` export +**2. Return from `headers` export** Headers from loaders and actions are not sent automatically. You must explicitly return them from the `headers` export. @@ -71,7 +89,7 @@ export function headers({ One notable exception is `Set-Cookie` headers, which are automatically preserved from `headers`, `loader`, and `action` in parent routes, even without exporting `headers` from the child route. -## Merging with parent headers +### Merging with parent headers Consider these nested routes @@ -85,7 +103,7 @@ If both route modules want to set headers, the headers from the deepest matching When you need to keep both the parent and the child headers, you need to merge them in the child route. -### Appending +#### Appending The easiest way is to simply append to the parent headers. This avoids overwriting a header the parent may have set and both are important. @@ -98,7 +116,7 @@ export function headers({ parentHeaders }: HeadersArgs) { } ``` -### Setting +#### Setting Sometimes it's important to overwrite the parent header. Do this with `set` instead of `append`: @@ -114,7 +132,7 @@ export function headers({ parentHeaders }: HeadersArgs) { You can avoid the need to merge headers by only defining headers in "leaf routes" (index routes and child routes without children) and not in parent routes. -## From `entry.server.tsx` +### From `entry.server.tsx` The `handleRequest` export receives the headers from the route module as an argument. You can append global headers here. From 7f10643ac431fb566e49a2b7f2c45015f73724b5 Mon Sep 17 00:00:00 2001 From: Remix Run Bot Date: Thu, 7 May 2026 14:56:36 +0000 Subject: [PATCH 2/3] chore: format --- contributors.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contributors.yml b/contributors.yml index aac5220763..d8291ba57f 100644 --- a/contributors.yml +++ b/contributors.yml @@ -378,12 +378,12 @@ - ryanhiebert - saengmotmi - SailorStat -- SAY-5 - samimsu - sanjai451 - sanketshah19 - sapphi-red - saul-atomrigs +- SAY-5 - sbolel - scarf005 - sealer3 From 7cd8cf834512fbaa3772a96ded3180743002737c Mon Sep 17 00:00:00 2001 From: Matt Brophy Date: Thu, 7 May 2026 11:25:16 -0400 Subject: [PATCH 3/3] docs: add basename note to useFormAction --- docs/api/hooks/useFormAction.md | 8 +++++++- packages/react-router/lib/dom/lib.tsx | 10 +++++++++- 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/docs/api/hooks/useFormAction.md b/docs/api/hooks/useFormAction.md index 7ffaf6711c..5c43bfe53f 100644 --- a/docs/api/hooks/useFormAction.md +++ b/docs/api/hooks/useFormAction.md @@ -28,7 +28,7 @@ the current URL of the app. This is used internally by [`Form`](../components/Form) to resolve the `action` to the closest route, but can be used generically as well. -```tsx +```ts import { useFormAction } from "react-router"; function SomeComponent() { @@ -40,6 +40,12 @@ function SomeComponent() { } ``` +This hook adds a `basename` if your app specifies one, so that it +can be used with raw `
` elements in a progressively enhanced way. If +you are using this to provide an `action` to `` or `fetcher.submit`, you +will need to remove the `basename` since both of those will prepend it +internally. + ## Signature ```tsx diff --git a/packages/react-router/lib/dom/lib.tsx b/packages/react-router/lib/dom/lib.tsx index 9f6dde80bf..bb819cb9fc 100644 --- a/packages/react-router/lib/dom/lib.tsx +++ b/packages/react-router/lib/dom/lib.tsx @@ -2628,7 +2628,7 @@ export function useSubmit(): SubmitFunction { * This is used internally by {@link Form} to resolve the `action` to the closest * route, but can be used generically as well. * - * @example + * ```ts * import { useFormAction } from "react-router"; * * function SomeComponent() { @@ -2638,6 +2638,14 @@ export function useSubmit(): SubmitFunction { * // closest route URL + "destroy" * let destroyAction = useFormAction("destroy"); * } + * ``` + * + * This hook adds a `basename` if your app specifies one, so that it + * can be used with raw `` elements in a progressively enhanced way. If + * you are using this to provide an `action` to `` or `fetcher.submit`, you + * will need to remove the `basename` since both of those will prepend it + * internally. + * * * @public * @category Hooks