Revalidation Strategies

Purpose and Scope

Revalidation is the refresh half of SWR’s stale-while-revalidate behavior. A component can render from an existing cache entry, then ask the fetcher to run again when SWR has a reason to believe that entry may be stale. This page organizes the revalidation triggers covered by the repository evidence: initial mount, explicit calls from application code, browser focus, network reconnect, and interval polling. It is written for developers trying to understand why a request fired, why a request did not fire, or which option should own a particular freshness policy.

Sources: test/use-swr-revalidate.test.tsx, test/use-swr-focus.test.tsx, test/use-swr-reconnect.test.tsx, test/use-swr-refresh.test.tsx

The main design distinction is automatic versus manual revalidation. Automatic revalidation is initiated by SWR in response to environment or timer signals, such as a hook mounting, a tab regaining focus, the browser coming back online, or a refresh interval elapsing. Manual revalidation is initiated by application code, most commonly with the bound mutation function returned from a hook. The tests exercise these paths with small components, event simulation, fake timers, delayed responses, and assertions about rendered values, which makes the behavior observable without relying on implementation internals.

Sources: test/use-swr-revalidate.test.tsx, test/use-swr-focus.test.tsx, test/use-swr-reconnect.test.tsx, test/use-swr-refresh.test.tsx

The example README files show why these mechanics exist in real applications. The focus revalidation example is framed as a basic authentication scenario where returning to a tab should refresh session-like data and where an individual hook can also be revalidated directly. The refetch interval example demonstrates automatic repeated API fetching so data remains up to date over time. These examples are intentionally small, but they map directly to common product needs: keeping identity, permissions, dashboards, background jobs, counters, or status panels current while preserving the cache-centered programming model.

Sources: examples/focus-revalidate/README.md, examples/refetch-interval/README.md

Relevant Source Files

  • test/use-swr-revalidate.test.tsx — Exercises bound mutate revalidation, revalidating all hooks with the same key, request sequencing, concurrent validating state, deduping interactions, mount settings, and paused validation conditions.
  • test/use-swr-focus.test.tsx — Verifies default focus revalidation, disabling it with revalidateOnFocus: false, stateful option changes, and throttling with focusThrottleInterval.
  • test/use-swr-reconnect.test.tsx — Verifies default reconnect revalidation, disabling it with revalidateOnReconnect: false, and gating reconnect behavior through online and visibility checks.
  • test/use-swr-refresh.test.tsx — Verifies polling through refreshInterval, interaction with dedupingInterval, runtime interval changes, timer cleanup, and stopped polling when an interval becomes zero.
  • examples/focus-revalidate/README.md — Provides the runnable focus revalidation example setup and describes the authentication-oriented focus and per-hook revalidation idea.
  • examples/refetch-interval/README.md — Provides the runnable interval refetch example setup and describes automatic repeated API fetching to keep data up to date.

Trigger Matrix

TriggerMain API or optionBehavior covered by evidenceWhen to use it
MountrevalidateOnMount and default hook startupInitial render moves from an empty placeholder to fetched data unless a test disables mount validationFirst load of a resource in a component
Explicit refreshBound mutate()A user action can rerun the fetcher and update the cache for that keyRefresh buttons, retries, save-then-refresh flows
Shared-key refreshSame SWR key in multiple hooksRevalidating one hook updates all subscribers with the same keySplit components reading the same resource
FocusrevalidateOnFocus, focusThrottleIntervalFocus revalidation is enabled by default, can be disabled, and can be throttledSession, profile, and tab-return freshness
ReconnectrevalidateOnReconnect, isOnline, visibilityReconnect revalidation is enabled by default but can be disabled or gatedRecovery after offline, sleep, or network changes
IntervalrefreshInterval, dedupingIntervalPolling updates after intervals while deduping prevents excessive requestsDashboards, counters, job status, live-ish views
Preload-adjacent freshnessLater hook participation in the cache lifecycleDetailed preload behavior is outside this page’s evidence, but later hooks can still revalidate by the same triggersData work started before render

Explicit Revalidation with mutate

The most direct manual strategy is to call the bound mutation function returned by a hook. In the revalidation tests, a component renders a button whose label contains the current value, and clicking that button calls the bound function without replacement data. The fetcher increments a local counter, so the rendered label changing from the first value to the next value proves that SWR invoked the fetcher again. This pattern is useful when the user explicitly asks for freshness, because the intent is visible in the component rather than hidden in a timer or browser event.

Sources: test/use-swr-revalidate.test.tsx

Manual revalidation is scoped by the SWR key rather than by the component instance that happened to call the function. One test renders two hooks with the same key, obtains the bound mutation function from one hook, and asserts that both displayed values update together after the click. That behavior is essential in larger screens where a resource is shown in a header, a details pane, and a sidebar at the same time. A single refresh action can update every subscriber to the key without building a separate cross-component notification system.

Sources: test/use-swr-revalidate.test.tsx

The revalidation suite also protects ordering and concurrent state. A race-condition test starts a slower revalidation and then a faster one, and the final rendered value comes from the later request rather than being overwritten by the older response. Another test starts overlapping delayed requests and keeps the validating indicator true while the second request is still in flight. For application code, this means refresh controls and loading affordances should be written around SWR’s validating state instead of assuming that a single request is the only possible in-flight operation.

Sources: test/use-swr-revalidate.test.tsx

Deduping is part of manual revalidation behavior, not only a performance detail. The tests include a short deduping window with delayed responses and verify that the latest meaningful result is still rendered. When a refresh button appears not to do anything, the cause may be timing, a shared key, or a deduping option rather than a broken fetcher. A good debugging sequence is to confirm the key, confirm which component owns the bound refresh action, inspect the deduping interval, and then reproduce the behavior with a delayed test fetcher.

Sources: test/use-swr-revalidate.test.tsx

Focus and Reconnect Revalidation

Focus revalidation handles the moment when the user returns to a page. The focus tests show that it is enabled by default: after the initial fetch renders the first value, focusing the window causes another fetch and the displayed value advances. This default is particularly useful for authentication, account, notification, or permission data because those resources can change while the user is away from the tab. The focus example README makes the same point at the recipe level by presenting a basic authentication example with focus-driven and per-hook revalidation.

Sources: test/use-swr-focus.test.tsx, examples/focus-revalidate/README.md

Focus behavior is configurable and can change while the component is mounted. A test with revalidateOnFocus: false proves that a focus event does not necessarily imply a new fetch. Another test stores the option in React state, toggles it through a click, and observes that later focus events follow the new setting. The throttle option gives another safeguard: focus events fired too close together are suppressed until the throttle window has passed. This prevents noisy tab, iframe, or visibility transitions from turning into unnecessary request bursts.

Sources: test/use-swr-focus.test.tsx

Reconnect revalidation covers a different environmental signal: the browser moves from offline to online. The reconnect tests simulate an offline event followed by an online event and show that the hook revalidates by default. This is the strategy you want when cached data remained visible while the network was unavailable, because the application can recover freshness automatically when connectivity returns. It is especially relevant for laptops waking from sleep, mobile devices moving between networks, or workflows where a user continues looking at stale data during a connection interruption.

Sources: test/use-swr-reconnect.test.tsx

Reconnect also has gates, and those gates are important for production control. The tests show that revalidateOnReconnect: false prevents the update, that an isOnline function returning false prevents revalidation, and that hidden-document state prevents the tested reconnect path from refreshing. These checks let applications define what “ready to fetch” means in their own environment. If reconnect behavior is surprising, inspect the revalidation option, the online predicate, and whether the document is visible before changing the fetcher or adding manual network listeners.

Sources: test/use-swr-reconnect.test.tsx

Interval Refresh and Polling

Interval refresh is the polling strategy. In the refresh tests, a hook configured with a refresh interval updates after fake timers advance by the configured amount. The same test also includes a deduping interval, showing that timer-driven refresh and duplicate request suppression operate together. The interval example README states the user-facing purpose plainly: make SWR fetch the API again automatically to ensure the data is up to date. This is the right fit when freshness is time-based rather than event-based.

Sources: test/use-swr-refresh.test.tsx, examples/refetch-interval/README.md

Polling is not a command to fetch blindly on every tick. A dedicated test combines a short refresh interval with a longer deduping interval and shows that ticks inside the deduping window do not advance the rendered value. Only after enough time has elapsed does another interval tick produce a new result. This distinction matters for dashboards and status pages, where developers often want regular updates but still need protection against request storms. The refresh interval describes desired cadence, while deduping describes the minimum useful separation for repeated work.

Sources: test/use-swr-refresh.test.tsx

The interval value can also change at runtime. One test stores the interval in component state, increases it in response to clicks, and eventually changes it to zero. The assertions show that SWR clears the old timer, creates the new timer, and stops polling when the interval becomes zero. This supports workflows where polling should be fast during an active operation, slower after a result stabilizes, or disabled after completion. Treat the interval as part of the component’s state machine rather than as a fixed global setting.

Sources: test/use-swr-refresh.test.tsx

Mount, Pausing, and Preload-Adjacent Workflows

Mount-time validation is visible across the tests as the transition from an initial placeholder render to the first fetched value. Some test components set revalidateOnMount: false to isolate later behavior, which demonstrates that startup fetching is itself a configurable revalidation decision. The revalidation tests also include a paused configuration case where initial validating state is affected by an isPaused condition. For real applications, this means first render behavior should be considered alongside focus, reconnect, interval, and manual refresh instead of being treated as a separate loading-only concern.

Sources: test/use-swr-revalidate.test.tsx, test/use-swr-refresh.test.tsx

Preload is adjacent to revalidation because it can start data work before a component renders, but the requested sources for this page do not contain the dedicated preload tests or implementation entrypoint. The safe operational framing is that once a hook participates in the cache lifecycle for a key, the same refresh triggers described here can make that cached value fresh again. If your workflow begins with preloading but later needs user-visible freshness, use the preload reference for startup semantics and this page for the subsequent focus, reconnect, interval, mount, or explicit refresh strategy.

Example Setup Commands

Use the focus example when you want a runnable demonstration of focus-triggered freshness and per-hook revalidation in an authentication-style flow. Use the refetch interval example when you want to observe timer-driven polling against an API route. Both README files use the same pattern: download only the example directory from the monorepo tarball, install dependencies, and start the local development server with either Yarn or npm. Once running, interact with the page and browser tab to connect the visible behavior back to the tests above.

Sources: examples/focus-revalidate/README.md, examples/refetch-interval/README.md

curl https://codeload.github.com/vercel/swr/tar.gz/main | tar -xz --strip=2 swr-main/examples/focus-revalidate
cd focus-revalidate
yarn
yarn dev
# or
npm install
npm run dev
curl https://codeload.github.com/vercel/swr/tar.gz/main | tar -xz --strip=2 swr-main/examples/refetch-interval
cd refetch-interval
yarn
yarn dev
# or
npm install
npm run dev

Implementation Guidance and Next Steps

Choose the narrowest trigger that matches the user experience. Use focus or reconnect when freshness should be tied to the user returning or the network recovering. Use interval refresh when the resource changes over time even if the user does nothing. Use explicit mutation when a button, retry action, save flow, or manual refresh should own the request. Combine triggers deliberately, because a single hook can be affected by mount behavior, focus events, reconnect events, polling, shared-key updates, and manual calls in the same component tree.

Sources: test/use-swr-revalidate.test.tsx, test/use-swr-focus.test.tsx, test/use-swr-reconnect.test.tsx, test/use-swr-refresh.test.tsx

When debugging unexpected rendering or request patterns, start with the key and then inspect the trigger configuration. A shared key lets one revalidation update multiple hook instances. A deduping interval can make a rapid manual refresh or interval tick appear to do nothing. A focus throttle can suppress repeated focus events. Reconnect behavior can be blocked by the reconnect option, the online detector, or document visibility. For deeper API details, continue with the pages for useSWR, mutate, preload, and the revalidation troubleshooting guide.