new·Earn with mozg — 20% of every monthSend somebody here and take a fifth of every plan payment they make, for as long as they keep paying — not a bounty on the first invoice. Your handle is the link, the window is thirty days, and the commission lands on your balance the second they pay. Free to join: if you have signed in, you already have the link. mozg.sh/earnall news →
mozg.beta
Sign in

React Router · Guides · all subjects

data loading

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

Fetchers enable concurrent data interactions without navigation

Fetchers are useful for creating complex, dynamic user interfaces that require multiple, concurrent data interactions without causing a navigation. Fetchers track their own independent state and can be used to load data, mutate data, submit forms, and generally interact with loaders and actions.

Display pending UI while form submission is in flight

Render pending UI by checking fetcher.state !== 'idle'. When the state is not idle, the fetcher is performing async work. Example: {fetcher.state !== 'idle' && <p>Saving...</p>}

fetcher.formData provides form values during submission

Access the submitted form data with fetcher.formData during submission. This allows rendering optimistic UI with the form values before the action completes. Example: let title = fetcher.formData?.get('title') || data.title;

Optimistic UI pattern with fetcher data

Render the next state immediately using form data available in fetcher.formData before the action completes. This provides instant feedback to the user while the mutation is in flight. Example: let title = fetcher.formData?.get('title') || data.title; then render <h1>{title}</h1>

fetcher.data contains action return value

Data returned from an action is available in the fetcher.data property. This is primarily useful for returning error messages to the user for a failed mutation.

fetcher.submit programmatically submits a form

Use fetcher.submit to submit a form programmatically. Pass the form element as the first argument: fetcher.submit(event.currentTarget.form). The fetcher will use the form's attributes and serialize data from its elements.

Search on user input with fetcher.submit

Implement search on input change by calling fetcher.submit in an onChange handler: onChange={(event) => { fetcher.submit(event.currentTarget.form); }}. This allows real-time search as the user types.

fetcher.submit accepts defaultShouldRevalidate option

Pass defaultShouldRevalidate option to fetcher.submit to control loader revalidation: fetcher.submit(form, { defaultShouldRevalidate: false }). Set to false to skip loader revalidation for that submission.

Fetcher loading data pattern with GET method

Use fetcher.Form with method='get' and action pointing to a route to load data. Example: <fetcher.Form method='get' action='/search-users'><input type='text' name='q' /></fetcher.Form>. The query parameter name must match the input name attribute.

Import type from loader for type inference

Use import type to import only types from a loader module to avoid circular dependencies: import type { loader } from './search-users';. Then pass to useFetcher: let fetcher = useFetcher<typeof loader>();

Render visual feedback while fetcher loads data

Adjust UI opacity or other styles based on fetcher.state to provide visual feedback. Example: style={{ opacity: fetcher.state === 'idle' ? 1 : 0.25 }} to dim content while loading.

useFetcher hook creates a fetcher instance

Import and call useFetcher to create a fetcher instance. You can optionally pass a type parameter for type inference: useFetcher<typeof loader>() to infer the return type of a loader.

fetcher.Form submits to action without navigation

Use fetcher.Form component to render a form that submits to an action without causing navigation. Set method and optionally action attributes. The fetcher will call the action and revalidate route data automatically.

fetcher.state tracks submission status

The fetcher.state property indicates the current state of the fetcher. You can check if fetcher.state !== 'idle' to render pending UI while async work is in progress.

Skipping revalidation can leave UI out of sync

Skipping revalidation can leave the UI out of sync with the server. It is recommended to prefer targeting a specific action or navigation and fall back to defaultShouldRevalidate instead of always returning false.

shouldRevalidate example for skipping analytics action

Example of conditional shouldRevalidate: check if formMethod is 'POST' and formAction ends with '/analytics', return false to skip revalidation; otherwise return defaultShouldRevalidate.

Form component with defaultShouldRevalidate example

Example: <Form method="post" action="/analytics" defaultShouldRevalidate={false}><button>Track Click</button></Form> skips revalidation for this form submission.

fetcher.submit with defaultShouldRevalidate example

Example: fetcher.submit({ intent: 'save-progress' }, { method: 'post', action: '/save-progress', defaultShouldRevalidate: false }) skips revalidation for this submission.

Link component with defaultShouldRevalidate example

Example: <Link to="/search?q=shoes" defaultShouldRevalidate={false}>Search Shoes</Link> skips revalidation for this navigation.

Default revalidation behavior in Framework Mode with SSR

In Framework Mode with SSR, the default behavior is opt-out: active loaders are revalidated on navigations and successful submissions (Link, Form, fetcher.submit). Failed submissions returning 4xx/5xx status do not trigger revalidations by default.

Default revalidation behavior in Framework SPA Mode and Data Mode

In Framework SPA Mode and Data Mode, the default behavior differs for different navigation types. For successful submissions (Form, fetcher.submit), it defaults to opt-out behavior and active loaders are revalidated. Failed submissions returning 4xx/5xx status do not trigger revalidations by default. For GET navigations (Link), it defaults to opt-in behavior where active loaders are only revalidated if their dynamic params changed, or if any search params changed. A GET navigation to the exact same URL is treated like a page refresh and all loaders are revalidated.

Child route revalidation skips do not skip ancestor routes

Matched routes are handled independently. A child that skips revalidation does not skip any ancestor routes.

fetcher.load revalidation behavior

fetcher.load only revalidates by default after action submissions and explicit useRevalidator calls, not on search-param or param-driven navigations.

Resource route fetch does not revalidate loaders

A plain fetch() to a resource route does not go through the router, so it does not revalidate loaders.

Export shouldRevalidate from route module to skip revalidation

Export shouldRevalidate from the route module in Framework Mode or set it on the route object in Data Mode. Returning false skips that route's loader. Always returning false opts that route out of the default behavior completely, including cases you usually still want like param changes or explicit useRevalidator calls.

shouldRevalidate function signature and key arguments

The shouldRevalidate function receives ShouldRevalidateFunctionArgs which includes: formMethod, formAction, defaultShouldRevalidate, formData, json, text (submission body), actionResult, actionStatus (action's return value), currentUrl, nextUrl, currentParams, nextParams (navigation details).

Conditional shouldRevalidate with defaultShouldRevalidate pattern

Inspect ShouldRevalidateFunctionArgs and return defaultShouldRevalidate for everything else to conditionally skip specific requests while maintaining default behavior for other cases. This is preferred over always returning false.

Skip revalidation for search param changes only

To ignore search-param-only updates while still revalidating when the pathname changes, compare currentUrl.pathname with nextUrl.pathname in shouldRevalidate and return false only when pathnames are identical.

Pass defaultShouldRevalidate at call site

Pass defaultShouldRevalidate={false} at the call site so you do not have to change every route file. This works on Form, Link, fetcher.Form, and as an option to useSubmit, fetcher.submit, useNavigate, and useSearchParams.

Route shouldRevalidate takes precedence over call site defaultShouldRevalidate

If a matched route does not export shouldRevalidate, the call site defaultShouldRevalidate value is used directly for that loader. If the route does export shouldRevalidate, the call site value is passed in as defaultShouldRevalidate and the route still has the final say.

Child shouldRevalidate returning false cannot hide root reload

A child shouldRevalidate that always returns false cannot hide a root reload after fetcher.submit. Either also opt root out for that case, or pass defaultShouldRevalidate: false at the call site when root has no shouldRevalidate of its own.

Give your agent this brain