DevToolBoxGRATIS
Blogg

SWR Complete Guide: Stale-While-Revalidate Data Fetching for React (2026)

19 min readby DevToolBox Team

SWR is a React Hooks library for data fetching created by Vercel. The name SWR comes from stale-while-revalidate, an HTTP cache invalidation strategy that first returns cached (stale) data, then sends the fetch request (revalidate), and finally delivers the up-to-date result. SWR provides a simple yet powerful API for fetching, caching, and revalidating data in React applications with built-in support for pagination, prefetching, SSR, and real-time updates.

TL;DR

SWR is a lightweight React data fetching library by Vercel that implements the stale-while-revalidate caching strategy. It provides automatic caching, request deduplication, focus revalidation, interval polling, optimistic UI updates, pagination with useSWRInfinite, SSR/SSG support for Next.js, and TypeScript-first design. At under 4KB gzipped, SWR delivers a fast and reliable data layer for modern React applications.

Key Takeaways
  • SWR returns cached data instantly while revalidating in the background, providing a snappy user experience with always-fresh data.
  • Built-in request deduplication means multiple components using the same key share a single network request automatically.
  • Focus revalidation, interval polling, and reconnect revalidation keep data fresh without manual intervention.
  • useSWRMutation separates remote mutations from data fetching, enabling optimistic updates and rollback on error.
  • useSWRInfinite provides a first-class API for infinite scrolling and paginated data loading patterns.
  • SWR integrates seamlessly with Next.js for server-side rendering and static generation with client-side revalidation.

What Is SWR and How Does Stale-While-Revalidate Work?

SWR is a strategy where the cache (stale data) is returned immediately when a request is made, then a revalidation request is fired in the background, and finally the cache is updated with the fresh response. This means users see data instantly from cache while the library quietly ensures that data stays up to date.

The useSWR hook is the core API. You give it a key (usually a URL) and a fetcher function. SWR handles caching, deduplication, revalidation, error retries, and focus tracking automatically. When the same key is used across multiple components, they share the same cache entry and only one network request is made.

import useSWR from 'swr';

// Define a fetcher function
const fetcher = (url) => fetch(url).then((res) => res.json());

function UserProfile({ userId }) {
  const { data, error, isLoading } = useSWR(
    '/api/users/' + userId,
    fetcher
  );

  if (isLoading) return <div>Loading...</div>;
  if (error) return <div>Error loading user</div>;

  return (
    <div>
      <h1>{data.name}</h1>
      <p>{data.email}</p>
    </div>
  );
}

SWR vs React Query (TanStack Query) Comparison

SWR and TanStack Query (formerly React Query) are the two most popular data fetching libraries for React. Both solve similar problems but take different approaches in complexity and feature scope.

FeatureSWRTanStack Query
Bundle Size~4KB gzipped~13KB gzipped
CachingStale-while-revalidateStale-while-revalidate + GC
DevtoolsCommunity (SWRDevTools)Official (built-in)
MutationsuseSWRMutation + mutateuseMutation (full lifecycle)
PaginationuseSWRInfiniteuseInfiniteQuery + paginated
SSR SupportNext.js first-classFramework-agnostic SSR
TypeScriptExcellent (built-in)Excellent (built-in)
Garbage CollectionManual (cache provider)Automatic (configurable)
Offline SupportBasic (via config)Advanced (mutations queue)
Learning CurveLow (simple API)Medium (more concepts)

Basic Usage: useSWR Hook

The useSWR hook takes a key and a fetcher function. The key is a unique string that identifies the data (typically an API URL). The fetcher is a function that actually performs the data retrieval. SWR returns data, error, isLoading, and isValidating states.

The fetcher function receives the key as its argument and returns a Promise. You can use fetch, axios, graphql-request, or any async function as your fetcher.

import useSWR from 'swr';

// Fetcher with error handling
const fetcher = async (url) => {
  const res = await fetch(url);
  if (!res.ok) {
    const error = new Error('Failed to fetch');
    error.info = await res.json();
    error.status = res.status;
    throw error;
  }
  return res.json();
};

function Dashboard() {
  // data: response data (undefined if not yet loaded)
  // error: error thrown by fetcher (undefined if no error)
  // isLoading: true on first fetch with no cached data
  // isValidating: true whenever a request is in flight
  const { data, error, isLoading, isValidating, mutate } = useSWR(
    '/api/dashboard',
    fetcher,
    {
      revalidateOnFocus: true,
      revalidateOnReconnect: true,
      refreshInterval: 30000, // poll every 30 seconds
    }
  );

  if (isLoading) return <div>Loading dashboard...</div>;
  if (error) return <div>Error: {error.message}</div>;

  return (
    <div>
      <h1>Dashboard</h1>
      {isValidating && <span>Refreshing...</span>}
      <p>Total users: {data.userCount}</p>
      <p>Revenue: {data.revenue}</p>
      <button onClick={() => mutate()}>Refresh</button>
    </div>
  );
}

Global Configuration with SWRConfig

SWRConfig provides a context for setting default options for all SWR hooks within your application. This avoids repeating the same fetcher, error retry logic, or revalidation intervals across every useSWR call.

You can nest SWRConfig providers to override options for specific sections of your app. Child configs merge with parent configs, so you only need to specify the options you want to change.

import { SWRConfig } from 'swr';

const globalFetcher = (url) => fetch(url).then((r) => r.json());

function App() {
  return (
    <SWRConfig
      value={{
        fetcher: globalFetcher,
        revalidateOnFocus: true,
        revalidateOnReconnect: true,
        dedupingInterval: 2000,
        errorRetryCount: 3,
        onError: (error, key) => {
          if (error.status === 403) {
            // Handle auth errors globally
            redirectToLogin();
          }
        },
      }}
    >
      <Dashboard />
    </SWRConfig>
  );
}

// No fetcher needed — uses global config
function Dashboard() {
  const { data } = useSWR('/api/dashboard');
  return <div>{data?.title}</div>;
}

Conditional Fetching

SWR supports conditional fetching by passing null or a falsy value as the key. When the key is null, SWR will not start the request. This is useful for scenarios where data depends on user authentication, feature flags, or other runtime conditions.

You can also pass a function as the key. If the function throws an error or returns a falsy value, SWR pauses the request. This is the recommended pattern for dependent fetching.

// Pass null to disable fetching
function ProtectedData({ isLoggedIn }) {
  const { data } = useSWR(
    isLoggedIn ? '/api/protected' : null,
    fetcher
  );
  if (!isLoggedIn) return <div>Please log in</div>;
  return <div>{data?.message}</div>;
}

// Use a function key that throws when dependency is missing
function UserOrders({ userId }) {
  const { data } = useSWR(
    () => userId ? '/api/users/' + userId + '/orders' : null,
    fetcher
  );
  return <div>{data?.orders.length} orders</div>;
}

Dependent Requests

Dependent requests are fetches that depend on the result of a previous request. SWR handles this elegantly by using a function as the key. If the function throws (because the dependency is not yet available), SWR pauses until the dependency resolves.

SWR guarantees that dependent requests are made in the correct order and that each step is properly cached. When the parent data changes, the dependent request automatically revalidates.

function UserDashboard() {
  // First: fetch the current user
  const { data: user } = useSWR('/api/me', fetcher);

  // Second: fetch projects only when user is loaded
  const { data: projects } = useSWR(
    () => user ? '/api/users/' + user.id + '/projects' : null,
    fetcher
  );

  // Third: fetch team only when user is loaded
  const { data: team } = useSWR(
    () => user ? '/api/teams/' + user.teamId : null,
    fetcher
  );

  if (!user) return <div>Loading user...</div>;

  return (
    <div>
      <h1>Welcome, {user.name}</h1>
      <p>{projects?.length ?? '...'} projects</p>
      <p>Team: {team?.name ?? 'Loading...'}</p>
    </div>
  );
}

Mutation: mutate and useSWRMutation

SWR provides two approaches for modifying remote data. The bound mutate function (returned by useSWR) revalidates the specific key. The global mutate function can revalidate any key. For dedicated mutations with loading states, use the useSWRMutation hook.

Bound Mutate

The mutate function returned by useSWR is bound to the current key. Call it to revalidate the data or pass new data to update the cache directly.

function TodoList() {
  const { data: todos, mutate } = useSWR('/api/todos', fetcher);

  const addTodo = async (text) => {
    // Revalidate after mutation (refetch from server)
    await fetch('/api/todos', {
      method: 'POST',
      body: JSON.stringify({ text }),
    });
    mutate(); // triggers revalidation
  };

  // Or update cache directly without revalidation
  const toggleTodo = async (id) => {
    await fetch('/api/todos/' + id + '/toggle', { method: 'PATCH' });
    mutate(
      todos.map((t) => t.id === id ? { ...t, done: !t.done } : t),
      false // set to false to skip revalidation
    );
  };

  return todos?.map((todo) => (
    <div key={todo.id} onClick={() => toggleTodo(todo.id)}>
      {todo.text}
    </div>
  ));
}

Global Mutate

The global mutate function from useSWRConfig can mutate any key from anywhere in your application. This is useful for updating related caches after a mutation.

import { useSWRConfig } from 'swr';

function CreateProject() {
  const { mutate } = useSWRConfig();

  const handleCreate = async (formData) => {
    await fetch('/api/projects', {
      method: 'POST',
      body: JSON.stringify(formData),
    });

    // Revalidate the projects list
    mutate('/api/projects');

    // Revalidate all keys matching a filter
    mutate(
      (key) => typeof key === 'string' && key.startsWith('/api/projects'),
      undefined,
      { revalidate: true }
    );
  };

  return <button onClick={() => handleCreate({ name: 'New' })}>Create</button>;
}

useSWRMutation Hook

useSWRMutation is designed for remote mutations (POST, PUT, DELETE). Unlike useSWR, it does not automatically fetch data. Instead, it provides a trigger function that you call manually.

import useSWRMutation from 'swr/mutation';

// Mutation fetcher receives (key, { arg })
async function createTodo(url, { arg }) {
  const res = await fetch(url, {
    method: 'POST',
    body: JSON.stringify(arg),
  });
  return res.json();
}

function AddTodo() {
  const { trigger, isMutating, error } = useSWRMutation(
    '/api/todos',
    createTodo
  );

  const handleSubmit = async (e) => {
    e.preventDefault();
    try {
      const newTodo = await trigger({ text: 'Buy groceries' });
      console.log('Created:', newTodo);
    } catch (err) {
      console.error('Failed:', err);
    }
  };

  return (
    <form onSubmit={handleSubmit}>
      <button disabled={isMutating}>
        {isMutating ? 'Adding...' : 'Add Todo'}
      </button>
      {error && <span>Error: {error.message}</span>}
    </form>
  );
}

Optimistic Updates

Optimistic updates improve perceived performance by updating the UI immediately before the server confirms the change. If the server request fails, SWR automatically rolls back to the previous data. This pattern is essential for responsive UIs in collaborative and real-time applications.

function LikeButton({ postId }) {
  const { data, mutate } = useSWR('/api/posts/' + postId, fetcher);

  const handleLike = async () => {
    // Optimistic update: immediately show the new state
    const optimisticData = { ...data, likes: data.likes + 1, liked: true };

    await mutate(
      async () => {
        // This runs in the background
        const res = await fetch('/api/posts/' + postId + '/like', {
          method: 'POST',
        });
        return res.json();
      },
      {
        optimisticData,
        rollbackOnError: true, // revert if server fails
        populateCache: true,
        revalidate: false,
      }
    );
  };

  return (
    <button onClick={handleLike}>
      {data?.liked ? 'Liked' : 'Like'} ({data?.likes})
    </button>
  );
}

Pagination with useSWRInfinite

useSWRInfinite is purpose-built for paginated and infinite scrolling interfaces. It manages an array of pages, each with its own cache key. You provide a getKey function that returns the key for each page index, and SWR handles loading, caching, and merging pages automatically.

The hook returns size (current number of pages loaded), setSize (function to load more), and the combined data array. It also supports parallel fetching of multiple pages for faster loading.

import useSWRInfinite from 'swr/infinite';

const PAGE_SIZE = 10;

// getKey receives page index and previous page data
function getKey(pageIndex, previousPageData) {
  // Reached the end
  if (previousPageData && !previousPageData.length) return null;

  // First page, no previous data
  if (pageIndex === 0) return '/api/posts?limit=' + PAGE_SIZE;

  // Use cursor from last item
  return '/api/posts?cursor='
    + previousPageData[previousPageData.length - 1].id
    + '&limit=' + PAGE_SIZE;
}

function PostFeed() {
  const { data, size, setSize, isValidating, isLoading } =
    useSWRInfinite(getKey, fetcher);

  // Flatten all pages into a single array
  const posts = data ? data.flat() : [];
  const isEnd = data && data[data.length - 1]?.length < PAGE_SIZE;
  const isLoadingMore = isLoading
    || (size > 0 && data && typeof data[size - 1] === 'undefined');

  return (
    <div>
      {posts.map((post) => (
        <article key={post.id}>{post.title}</article>
      ))}
      <button
        onClick={() => setSize(size + 1)}
        disabled={isLoadingMore || isEnd}
      >
        {isLoadingMore ? 'Loading...' : isEnd ? 'No more' : 'Load More'}
      </button>
    </div>
  );
}

Prefetching Data

SWR supports several prefetching patterns to load data before the user needs it. You can prefetch on hover, on route change, or at the application level. Prefetched data is stored in the cache and instantly returned when a component mounts with the same key.

The preload function from SWR triggers a fetch and stores the result in cache. When a component later calls useSWR with the same key, the cached data is returned immediately without any loading state.

import { preload } from 'swr';

// Prefetch on module load
preload('/api/config', fetcher);

// Prefetch on hover
function ProjectLink({ projectId }) {
  return (
    <a
      href={'/projects/' + projectId}
      onMouseEnter={() =>
        preload('/api/projects/' + projectId, fetcher)
      }
    >
      View Project
    </a>
  );
}

// The component uses the prefetched data instantly
function ProjectDetail({ projectId }) {
  const { data } = useSWR('/api/projects/' + projectId, fetcher);
  // If preloaded, data is available immediately
  return <div>{data?.name}</div>;
}

Revalidation Strategies

SWR provides multiple automatic revalidation strategies to keep data fresh without manual intervention. These can be configured globally or per-hook.

Revalidate on Focus

When the user switches back to your browser tab, SWR automatically revalidates all active data. This ensures that stale data from background tabs is refreshed. Enabled by default.

Revalidate on Interval

Set a polling interval to periodically refresh data. Useful for dashboards, live feeds, and real-time data. SWR is smart enough to pause polling when the tab is not visible.

Revalidate on Reconnect

When the browser regains network connectivity, SWR automatically revalidates all data. This ensures that data fetched during an offline period is updated once the connection is restored.

// Configure revalidation strategies
const { data } = useSWR('/api/stock-price', fetcher, {
  // Revalidate on window focus (default: true)
  revalidateOnFocus: true,

  // Poll every 5 seconds
  refreshInterval: 5000,

  // Pause polling when tab is hidden
  refreshWhenHidden: false,

  // Continue polling when offline (default: false)
  refreshWhenOffline: false,

  // Revalidate on network reconnect (default: true)
  revalidateOnReconnect: true,

  // Skip revalidation on mount if cached data exists
  revalidateIfStale: true,
});

// useSWRImmutable — for data that never changes
import useSWRImmutable from 'swr/immutable';

const { data: config } = useSWRImmutable('/api/config', fetcher);
// Equivalent to:
// useSWR(key, fetcher, {
//   revalidateIfStale: false,
//   revalidateOnFocus: false,
//   revalidateOnReconnect: false,
// })

Error Handling and Retry

SWR provides built-in error handling with automatic retry. When a fetcher throws an error, SWR catches it and exposes it through the error return value. By default, SWR retries failed requests using exponential backoff.

You can customize retry behavior with onErrorRetry, set a maximum retry count, or disable retries entirely. SWR also supports global error handling through the onError callback in SWRConfig.

import { SWRConfig } from 'swr';

// Global error retry configuration
<SWRConfig
  value={{
    onErrorRetry: (error, key, config, revalidate, { retryCount }) => {
      // Never retry on 404
      if (error.status === 404) return;

      // Never retry for a specific key
      if (key === '/api/user') return;

      // Stop after 10 retries
      if (retryCount >= 10) return;

      // Retry with exponential backoff
      setTimeout(
        () => revalidate({ retryCount }),
        Math.min(1000 * Math.pow(2, retryCount), 30000)
      );
    },
  }}
>
  <App />
</SWRConfig>

// Component-level error handling
function UserData() {
  const { data, error, isLoading } = useSWR('/api/user', fetcher, {
    errorRetryCount: 3,
    errorRetryInterval: 5000,
  });

  if (error) {
    return (
      <div role="alert">
        <h2>Something went wrong</h2>
        <p>{error.message}</p>
        <pre>{JSON.stringify(error.info, null, 2)}</pre>
      </div>
    );
  }

  return <div>{data?.name}</div>;
}

TypeScript Integration

SWR is written in TypeScript and provides excellent type inference out of the box. The useSWR hook is generic, allowing you to specify the data type and error type. When you provide a typed fetcher, SWR automatically infers the return type.

For more complex scenarios, you can use SWRResponse, Key, Fetcher, and other exported types to build strongly typed abstractions on top of SWR.

import useSWR, { Fetcher, SWRResponse } from 'swr';

// Define your data types
interface User {
  id: number;
  name: string;
  email: string;
}

interface ApiError {
  message: string;
  status: number;
}

// Typed fetcher
const fetcher: Fetcher<User, string> = async (url) => {
  const res = await fetch(url);
  if (!res.ok) throw { message: 'Not found', status: res.status };
  return res.json();
};

// SWR infers data as User and error as ApiError
function UserProfile({ id }: { id: number }) {
  const { data, error } = useSWR<User, ApiError>(
    '/api/users/' + id,
    fetcher
  );

  // data is User | undefined
  // error is ApiError | undefined
  return <div>{data?.name}</div>;
}

// Reusable typed hook
function useUser(id: number): SWRResponse<User, ApiError> {
  return useSWR<User, ApiError>('/api/users/' + id, fetcher);
}

Server-Side Rendering with Next.js

SWR integrates deeply with Next.js for server-side rendering and static generation. The fallback option in SWRConfig lets you pass server-fetched data to useSWR hooks on the client. The client receives the pre-fetched data immediately and then revalidates in the background.

For App Router (Server Components), you can fetch data on the server and pass it as the fallback prop. SWR will use this data for the initial render and revalidate client-side.

// app/page.tsx (Server Component)
import { UserList } from './user-list';

export default async function Page() {
  // Fetch on the server
  const users = await fetch('https://api.example.com/users').then(
    (r) => r.json()
  );

  return (
    <UserList fallbackData={users} />
  );
}

// app/user-list.tsx (Client Component)
'use client';
import { SWRConfig } from 'swr';
import useSWR from 'swr';

function UserListInner() {
  // Uses fallback data on initial render, then revalidates
  const { data: users } = useSWR('/api/users', fetcher);
  return users?.map((u) => <div key={u.id}>{u.name}</div>);
}

export function UserList({ fallbackData }) {
  return (
    <SWRConfig value={{ fallback: { '/api/users': fallbackData } }}>
      <UserListInner />
    </SWRConfig>
  );
}

Caching and Deduplication

SWR uses an in-memory cache by default, keyed by the string key you pass to useSWR. When multiple components use the same key, they all share the same cache entry. SWR deduplicates concurrent requests automatically: if three components mount simultaneously with the same key, only one network request is made.

You can customize the cache provider to use localStorage, IndexedDB, or any storage backend. The dedupingInterval option controls how long SWR considers requests as duplicates (default is 2 seconds).

import { SWRConfig } from 'swr';

// Custom cache provider with localStorage persistence
function localStorageCacheProvider() {
  const map = new Map(
    JSON.parse(localStorage.getItem('swr-cache') || '[]')
  );

  // Save to localStorage before unload
  window.addEventListener('beforeunload', () => {
    const entries = JSON.stringify(Array.from(map.entries()));
    localStorage.setItem('swr-cache', entries);
  });

  return map;
}

function App() {
  return (
    <SWRConfig
      value={{
        provider: localStorageCacheProvider,
        dedupingInterval: 5000, // 5-second dedup window
      }}
    >
      <Dashboard />
    </SWRConfig>
  );
}

SWR Middleware

SWR middleware lets you intercept and modify the behavior of useSWR hooks. A middleware is a function that wraps the useSWR hook, giving you access to the key, fetcher, and options before and after the hook executes. This enables logging, analytics, request modification, and custom caching logic.

// Logging middleware
function logger(useSWRNext) {
  return (key, fetcher, config) => {
    const swr = useSWRNext(key, fetcher, config);

    // Log whenever data changes
    React.useEffect(() => {
      console.log('[SWR]', key, swr.data);
    }, [key, swr.data]);

    return swr;
  };
}

// Auth token injection middleware
function withAuth(useSWRNext) {
  return (key, fetcher, config) => {
    const authFetcher = (url) => {
      const token = getAuthToken();
      return fetch(url, {
        headers: { Authorization: 'Bearer ' + token },
      }).then((r) => r.json());
    };
    return useSWRNext(key, authFetcher, config);
  };
}

// Use middleware globally or per-hook
<SWRConfig value={{ use: [logger, withAuth] }}>
  <App />
</SWRConfig>

// Or per-hook
useSWR('/api/data', fetcher, { use: [logger] });

Testing SWR Components

Testing components that use SWR requires wrapping them in an SWRConfig with a fresh cache provider to isolate tests. You can mock the fetcher, pre-populate the cache with fallback data, or use a mock service worker for network-level mocking.

import { render, screen, waitFor } from '@testing-library/react';
import { SWRConfig } from 'swr';

// Wrapper to isolate SWR cache between tests
function SWRWrapper({ children }) {
  return (
    <SWRConfig
      value={{
        provider: () => new Map(),
        dedupingInterval: 0,
      }}
    >
      {children}
    </SWRConfig>
  );
}

// Test with mock fetcher
test('renders user name', async () => {
  const mockUser = { id: 1, name: 'Alice' };

  render(
    <SWRConfig value={{
      provider: () => new Map(),
      fallback: { '/api/user': mockUser },
    }}>
      <UserProfile />
    </SWRConfig>
  );

  await waitFor(() => {
    expect(screen.getByText('Alice')).toBeInTheDocument();
  });
});

Best Practices

  • Define your fetcher once in SWRConfig rather than repeating it in every useSWR call to keep your code DRY.
  • Use stable key strings. Avoid constructing keys with objects or arrays because SWR uses string comparison for cache keys.
  • Use useSWRMutation for POST/PUT/DELETE operations instead of manually calling mutate after fetch.
  • Enable optimistic updates for user-facing mutations (likes, comments, toggles) to improve perceived performance.
  • Use useSWRInfinite for paginated data instead of manually managing page state with useSWR.
  • Set appropriate dedupingInterval values to prevent excessive network requests during rapid component mounting.
  • Wrap test components in SWRConfig with a fresh Map cache provider to prevent test pollution.
  • Leverage the fallback option in SWRConfig for SSR and preloading data from the server.
  • Use conditional fetching (null key) instead of adding if-statements around useSWR to avoid React hooks order issues.
  • Use middleware for cross-cutting concerns like logging, analytics, and authentication token injection.

Frequently Asked Questions

What does SWR stand for?

SWR stands for stale-while-revalidate. It is an HTTP cache invalidation strategy defined in RFC 5861. The approach returns cached (stale) data first for instant display, then fetches fresh data in the background, and finally swaps in the updated result. This gives users an immediate response while ensuring data freshness.

How is SWR different from React Query (TanStack Query)?

SWR is smaller (4KB vs 13KB gzipped) and has a simpler API focused on data fetching with the stale-while-revalidate pattern. TanStack Query offers more features including built-in devtools, automatic garbage collection, advanced mutation lifecycle hooks, and offline mutation queuing. Choose SWR for simplicity and small bundle size; choose TanStack Query for complex data management needs.

Does SWR work with Next.js App Router and Server Components?

Yes. SWR works with Next.js App Router. You fetch data in Server Components and pass it as the fallback prop to SWRConfig in a Client Component. SWR uses the server data for the initial render and revalidates on the client. Note that useSWR itself must be used in Client Components since it is a React hook.

How does SWR handle request deduplication?

When multiple components call useSWR with the same key within the dedupingInterval (default 2 seconds), SWR only sends one network request. All components share the same cached data and update simultaneously when the response arrives. This happens automatically without any configuration.

Can I use SWR with GraphQL?

Yes. SWR is transport-agnostic. You can use any async function as the fetcher, including graphql-request, Apollo Client, or a custom fetch wrapper for GraphQL. The key can be the query string or a combination of query and variables. SWR handles caching and revalidation regardless of the underlying protocol.

How do I handle pagination with SWR?

Use the useSWRInfinite hook for paginated and infinite scroll interfaces. It accepts a getKey function that receives the page index and previous page data, and returns the key for each page. The hook manages an array of page responses and provides setSize to load more pages. It supports both offset-based and cursor-based pagination.

Does SWR support offline mode?

SWR provides basic offline support through its caching layer. Cached data remains available when the network is offline. When connectivity is restored, SWR automatically revalidates all data via the onReconnect event. For advanced offline support with persistent cache, you can implement a custom cache provider using localStorage or IndexedDB.

How do I prevent SWR from fetching on mount?

Pass revalidateOnMount: false in the options to prevent SWR from fetching when the component mounts. You can also use useSWRImmutable (or set revalidateOnFocus, revalidateOnReconnect, and revalidateIfStale all to false) for data that rarely changes. To conditionally skip fetching entirely, pass null as the key.

𝕏 Twitterin LinkedIn
Var detta hjälpsamt?

Håll dig uppdaterad

Få veckovisa dev-tips och nya verktyg.

Ingen spam. Avsluta när som helst.

Try These Related Tools

{ }JSON FormatterTSJSON to TypeScript

Related Articles

React Query Mönster 2026: Data Fetching, Caching och Mutations med TanStack Query

Behärska React Query (TanStack Query) mönster 2026: useQuery, useMutation, optimistiska uppdateringar.

React Design Patterns Guide: Compound Components, Custom Hooks, HOC, Render Props & State Machines

Complete React design patterns guide covering compound components, render props, custom hooks, higher-order components, provider pattern, state machines, controlled vs uncontrolled, composition, observer pattern, error boundaries, and module patterns.

Next.js Advanced Guide: App Router, Server Components, Data Fetching, Middleware & Performance

Complete Next.js advanced guide covering App Router architecture, React Server Components, streaming SSR, data fetching patterns, middleware, route handlers, parallel and intercepting routes, caching strategies, ISR, image optimization, and deployment best practices.