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 · Guides · all subjects

data-loading

87 notes in this subject, read out of this brain and free to use. This is page 2 of 2.

Headers from loaders and actions must be returned from headers export

Headers set in loaders and actions are not sent automatically. You must explicitly return them from the headers export function. The headers export receives actionHeaders and loaderHeaders in HeadersArgs and should conditionally return the appropriate headers.

Revalidation optimization during client-side transitions

During client-side transitions, React Router automatically optimizes reloading by not reloading layout routes that aren't changing. However, in cases like form submissions or search param changes, React Router reloads all routes to be safe, ensuring the UI stays in sync with the server state.

shouldRevalidate function for custom revalidation optimization

Apps can define a shouldRevalidate function on a route module to further optimize revalidation. This function receives calls from React Router on every navigation and every revalidation after an action is called. Returning false prevents React Router from reloading that route. If implemented incorrectly, this can cause the UI to get out of sync with the server, so it requires careful implementation.

fetcher.load revalidation behavior

fetcher.load calls trigger revalidation, but they load a specific URL so they do not need to worry about route param or URL search param revalidations. fetcher.load only revalidates by default after action submissions and explicit revalidation requests via useRevalidator.

Pre-rendering with ssr:true uses route loader functions

When pre-rendering with ssr:true, routes use the same route loader functions as server rendering. The build creates a new Request() and runs it through your app just like a server would. Requests to paths that have not been pre-rendered will be server rendered as usual.

Pre-rendered routes can have loaders with ssr:false

When using ssr:false with a prerender config, matched routes can have loaders because the build pre-renders all matched routes for those paths, not just the root. You cannot include actions or headers functions in any routes when ssr:false is set because there will be no runtime server.

Loaders and actions can return React elements in RSC Framework Mode

In RSC Framework Mode, loaders and actions can return React elements along with other data. These elements will only ever be rendered on the server. If they need to use client-only features like hooks or event handlers, extract those components into a client module with the "use client" directive.

Server Components as default in RSC Data Mode

In RSC Data Mode, each route's default export renders as a Server Component by default. Server Components can be async and fetch data directly from the component.

Server Functions with use server directive

Server Functions are defined with the "use server" directive and allow you to call async functions executed on the server. After server functions are called, React Router automatically revalidates the route and updates the UI with the new server content without manual cache invalidation.

Set status codes from loaders and actions

Use the `data` function from react-router to set HTTP status codes when returning from loaders and actions. The `data` function takes the response payload as the first argument and an options object with a `status` property as the second argument.

data() function for custom status codes

The `data` function is imported from 'react-router' and accepts two arguments: the response data and an options object. In the options object, the `status` property sets the HTTP status code. Example: `data({ message: 'Invalid title' }, { status: 400 })` returns a 400 Bad Request response.

Default status code is 200

When returning data from a loader or action without using the `data` function wrapper, the default HTTP status code is 200 OK. You only need to use `data` if you want to return a non-200 status code.

Common status codes in React Router

Common HTTP status codes used with React Router include: 200 (default for successful responses), 201 (created for successful POST requests), 400 (bad request for validation errors), and 404 (not found when a resource does not exist).

Streaming with Suspense basic pattern

React Router supports streaming with React Suspense by returning promises from loaders and actions. This allows apps to speed up initial renders by deferring non-critical data and unblocking UI rendering. Return unawaited promises from loaders for non-critical data, while awaiting critical data, then use the Await component with React.Suspense to render fallback UI while the promise resolves.

Loader return value must be object with keys not single promise

When returning promises from a loader for streaming, you cannot return a single promise directly. The promise must be returned as a property within an object with keys.

Streaming loader example with critical and non-critical data

Example loader that returns unawaited non-critical data and awaited critical data: ```tsx import type { Route } from "./+types/my-route"; export async function loader({}: Route.LoaderArgs) { // note this is NOT awaited let nonCriticalData = new Promise((res) => setTimeout(() => res("non-critical"), 5000), ); let criticalData = await new Promise((res) => setTimeout(() => res("critical"), 300), ); return { nonCriticalData, criticalData }; } ```

Await component renders streamed promise with Suspense

The Await component from react-router awaits a promise returned from loaderData and triggers React.Suspense to render the fallback UI. The resolved value is passed to the Await's render function (children as a function).

Streaming component example using Await

Example component that renders critical data immediately and non-critical data with a fallback: ```tsx import * as React from "react"; import { Await } from "react-router"; export default function MyComponent({ loaderData, }: Route.ComponentProps) { let { criticalData, nonCriticalData } = loaderData; return ( <div> <h1>Streaming example</h1> <h2>Critical data value: {criticalData}</h2> <React.Suspense fallback={<div>Loading...</div>}> <Await resolve={nonCriticalData}> {(value) => <h3>Non critical value: {value}</h3>} </Await> </React.Suspense> </div> ); } ```

React 19 React.use alternative to Await

With React 19, you can use React.use instead of the Await component. When using React.use, you must create a new component and pass the promise down to trigger the suspense fallback, since React.use can only be called inside a component.

React 19 streaming example with React.use

Example of streaming with React 19 using React.use: ```tsx <React.Suspense fallback={<div>Loading...</div>}> <NonCriticalUI p={nonCriticalData} /> </React.Suspense> ``` ```tsx function NonCriticalUI({ p }: { p: Promise<string> }) { let value = React.use(p); return <h3>Non critical value {value}</h3>; } ```

Default stream timeout 4950ms

By default, loaders and actions reject any outstanding promises after 4950ms. This timeout can be customized.

Configure streamTimeout in entry.server.tsx

Export a streamTimeout numerical value from entry.server.tsx to control when loaders and actions reject outstanding promises. For example: ```ts // Reject all pending promises from handler functions after 10 seconds export const streamTimeout = 10_000; ```

Promise.all awaits all loaders before streaming starts

React Router waits for all loaders to settle using Promise.all before it begins streaming the response. Once streaming has started, subsequent rejections of streamed promises are caught and surfaced to the Await error UI.

Early rejection pitfall in Node when child promise rejects before parent loader settles

If a streamed promise rejects before all of the route's loaders have settled, React Router has not yet been able to attach a handler to it. In Node, an unhandled promise rejection will crash the process unless you have a top-level handler registered. This can occur when a parent route's loader takes longer to resolve than a child route's streamed promise takes to reject.

future.v8_passThroughRequests passes raw HTTP request instance

By default, React Router normalizes request.url by removing .data suffixes and internal search parameters like ?index and ?_routes. This flag eliminates that normalization and passes the raw HTTP request instance to loader, action, and middleware functions. Benefits: reduces server-side overhead by eliminating multiple new Request() calls, and allows distinguishing document from data requests based on .data suffix presence.

Enable future.v8_passThroughRequests

In react-router.config.ts, add: export default { future: { v8_passThroughRequests: true } } satisfies Config;

Use url parameter for normalized routing with future.v8_passThroughRequests

When future.v8_passThroughRequests is enabled, use the url parameter (a normalized URL instance) for routing logic that should strip .data suffixes. Use request.url for raw routing logic and to distinguish between document and data requests. Example: const isDataRequest = new URL(request.url).pathname.endsWith('.data');

Give your agent this brain