---
title: Revalidation Optimization
---
# Revalidation Optimization
[MODES: framework, data]
After a mutation or some navigations, React Router re-runs loaders so
the UI stays in sync with the server. That default is the right
starting point. When a loader is expensive, or a mutation cannot
affect that route's data, you can skip the reload.
Skipping revalidation can leave the UI out of sync with the server.
Prefer targeting a specific action or navigation, and fall back to
`defaultShouldRevalidate` instead of always returning `false`.
## Default behavior
The default behavior differs between Framework and Data Modes:
- **Framework Mode with SSR**
- Defaults to opt-out behavior - active loaders are revalidated on navigations and successful submissions ([`Link`][link], [`Form`][form], [`fetcher.submit`](fetcher-submit))
- Failed submissions returning a 4xx/5xx status do not trigger revalidations by default
- **Framework "SPA Mode" and Data Mode**
- Defaults to opt-out behavior on successful submissions - active loaders are revalidated on successful submissions ([`Form`][form], [`fetcher.submit`])
- Failed submissions returning a 4xx/5xx status do not trigger revalidations by default
- Defaults to opt-in behavior for GET navigations ([`Link`][link]) - 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.
Matched matched routes are handled independently - A child that skips
revalidation does not skip any ancestor routes.
[`fetcher.load`][use-fetcher] only revalidates by default after action
submissions and explicit [`useRevalidator`][use-revalidator] calls, not
on search-param or param-driven navigations.
A plain `fetch()` to a [resource route][resource-routes] does not
go through the router, so it does not revalidate loaders.
## Skip a route with `shouldRevalidate`
Export `shouldRevalidate` from the [route module][route-module]
(Framework Mode) or set it on the [route object][data-mode]
(Data Mode). Returning `false` skips **that route's** loader.
```tsx filename=app/routes/dashboard.tsx
// Framework Mode
export function shouldRevalidate() {
return false;
}
```
```tsx src/main.tsx
// Data Mode
createBrowserRouter([
{
path: "/dashboard",
loader: dashboardLoader,
shouldRevalidate: () => false,
Component: Dashboard,
},
]);
```
Always returning `false` opts that route out of the default
behavior completely, including cases you usually still want
(param changes, explicit [`useRevalidator`][use-revalidator]).
Prefer the conditional form below.
## Opt out of specific requests
Inspect
[`ShouldRevalidateFunctionArgs`][should-revalidate-args]
and return `defaultShouldRevalidate` for everything else.
```tsx
import type { ShouldRevalidateFunctionArgs } from "react-router";
export function shouldRevalidate({
formMethod,
formAction,
defaultShouldRevalidate,
}: ShouldRevalidateFunctionArgs) {
if (
formMethod === "POST" &&
formAction?.endsWith("/analytics")
) {
return false;
}
return defaultShouldRevalidate;
}
```
Other useful fields:
- `formData`, `json`, `text` — the submission body
- `actionResult`, `actionStatus` — the action's return value
- `currentUrl`, `nextUrl`, `currentParams`, `nextParams` —
the navigation
You can ignore search-param-only updates while still
revalidating when the pathname changes:
```tsx
export function shouldRevalidate({
currentUrl,
nextUrl,
defaultShouldRevalidate,
}: ShouldRevalidateFunctionArgs) {
if (currentUrl.pathname === nextUrl.pathname) {
return false;
}
return defaultShouldRevalidate;
}
```
## Skip revalidation for one event
Pass `defaultShouldRevalidate={false}` at the call site so you
do not have to change every route file. This works on
[`