React SME Cookbook
All FAQs

Search Documentation

Search across all documentation pages

swrsetupconfigurationSWRConfig

SWR Setup and Configuration

Recipe

Install SWR, wrap your app with SWRConfig, and set global defaults so every useSWR call inherits a shared fetcher and options.

npm install swr
// app/providers.tsx
"use client";
 
import { SWRConfig } from "swr";
 
const fetcher = (url: string) => fetch(url).then((res) => res.json());
 
export function SWRProvider({ children }: { children: React.ReactNode }) {
  return (
    <SWRConfig
      value={{
        fetcher,
        revalidateOnFocus: true,
        dedupingInterval: 2000,
      }}
    >
      {children}
    </SWRConfig>
  );
}
// app/layout.tsx
import { SWRProvider } from "./providers";
 
export default function RootLayout({ children }: { children: React.ReactNode }) {
  return (
    <html lang="en">
      <body>
        <SWRProvider>{children}</SWRProvider>
      </body>
    </html>
  );
}

Working Example

"use client";
 
import useSWR from "swr";
 
function UserProfile({ userId }: { userId: string }) {
  // fetcher is inherited from SWRConfig
  const { data, error, isLoading } = useSWR(`/api/users/${userId}`);
 
  if (isLoading) return <div>Loading...</div>;
  if (error) return <div>Failed to load</div>;
 
  return <div>Hello, {data.name}!</div>;
}

Deep Dive

How It Works

  • SWRConfig uses React Context under the hood to propagate configuration values down the component tree.
  • Any useSWR call without an explicit fetcher will fall back to the nearest SWRConfig provider's fetcher.
  • Multiple SWRConfig providers can be nested; inner values merge with and override outer values.
  • The dedupingInterval prevents duplicate requests to the same key within the specified milliseconds.
  • revalidateOnFocus triggers a background revalidation when the browser tab regains focus.

Variations

Custom fetcher with headers:

const authedFetcher = (url: string) =>
  fetch(url, {
    headers: { Authorization: `Bearer ${getToken()}` },
  }).then((res) => {
    if (!res.ok) throw new Error("Fetch failed");
    return res.json();
  });

Nested config override:

<SWRConfig value={{ fetcher: globalFetcher }}>
  <SWRConfig value={{ refreshInterval: 3000 }}>
    {/* Components here get globalFetcher + 3s refresh */}
  </SWRConfig>
</SWRConfig>

Functional config (access parent):

<SWRConfig
  value={(parentConfig) => ({
    ...parentConfig,
    refreshInterval: 5000,
  })}
>
  {children}
</SWRConfig>

TypeScript Notes

  • SWRConfig accepts value typed as Partial<SWRConfiguration> or a function returning one.
  • Import SWRConfiguration from swr to type config objects explicitly.
import type { SWRConfiguration } from "swr";
 
const config: SWRConfiguration = {
  fetcher: (url: string) => fetch(url).then((r) => r.json()),
  revalidateOnFocus: false,
};

Gotchas

  • Forgetting to add "use client" on the provider file in Next.js App Router will cause a runtime error because SWRConfig uses React Context.
  • If your fetcher throws on non-2xx responses, SWR treats that as an error. If it does not throw, error will always be undefined even on 404s.
  • dedupingInterval is not the same as caching. It only deduplicates in-flight requests within the interval window.
  • Setting refreshInterval at the global level applies to every hook. Prefer setting it per-hook unless you genuinely need global polling.

Alternatives

ApproachProsCons
Global SWRConfigSingle setup, consistent defaultsAll hooks share config; can be too broad
Per-hook configFine-grained control per queryRepetitive fetcher declarations
Custom hook wrapperCentralized logic with flexibilityExtra abstraction layer

FAQs

Why does the SWRConfig provider file need "use client" in Next.js App Router?

SWRConfig uses React Context internally, which is a client-side API. Without "use client", Next.js treats the file as a Server Component and throws a runtime error when Context is used.

What does the dedupingInterval option actually do?
  • It deduplicates in-flight requests to the same key within the specified millisecond window (default 2000ms).
  • If two components mount and call useSWR with the same key within that window, only one network request is made.
  • It is not the same as caching -- it only prevents concurrent duplicate requests.
How do nested SWRConfig providers merge their values?

Inner SWRConfig values merge with and override outer values. For example, an inner provider can set refreshInterval while inheriting the outer provider's fetcher.

What happens if the fetcher does not throw on non-2xx HTTP responses?

SWR will treat the response as successful data. The error value will always be undefined, even on 404 or 500 responses. Always check res.ok in your fetcher and throw if it is false.

How can I access the parent SWRConfig values when defining a nested config?

Pass a function instead of an object to the value prop:

<SWRConfig
  value={(parentConfig) => ({
    ...parentConfig,
    refreshInterval: 5000,
  })}
>
  {children}
</SWRConfig>
Is it safe to set refreshInterval globally in SWRConfig?

It works, but it applies polling to every useSWR hook in the tree. Prefer setting refreshInterval per-hook unless you genuinely need all queries to poll at the same rate.

How do I type the SWRConfig value object in TypeScript?

Import SWRConfiguration from swr and use it as the type:

import type { SWRConfiguration } from "swr";
 
const config: SWRConfiguration = {
  fetcher: (url: string) => fetch(url).then((r) => r.json()),
  revalidateOnFocus: false,
};
What TypeScript type does the SWRConfig value prop accept?

It accepts Partial<SWRConfiguration> or a function (parentConfig: SWRConfiguration) => SWRConfiguration that receives the parent config and returns a new one.

Can I add authorization headers to the global fetcher?

Yes. Define a custom fetcher that includes headers:

const authedFetcher = (url: string) =>
  fetch(url, {
    headers: { Authorization: `Bearer ${getToken()}` },
  }).then((res) => {
    if (!res.ok) throw new Error("Fetch failed");
    return res.json();
  });
Gotcha: What is a common mistake when omitting the fetcher from individual useSWR calls?

If there is no SWRConfig provider wrapping the component (or the provider does not define a fetcher), useSWR will have no fetcher and silently never fetch data. Always ensure a provider with a fetcher exists above your components.

Where should the SWRProvider be placed in a Next.js App Router project?

Wrap it around {children} in your root app/layout.tsx so every page and component inherits the shared configuration.