DEV Community

Alex Spinov
Alex Spinov

Posted on

SWR Has a Free API That Makes Data Fetching in React Effortless

SWR (stale-while-revalidate) is Vercel's data-fetching library for React. Its API is deceptively simple — but under the hood, it handles caching, revalidation, pagination, and optimistic updates.

Basic Usage: One Hook, Full Cache

import useSWR from "swr";

const fetcher = (url: string) => fetch(url).then(r => r.json());

function Profile() {
  const { data, error, isLoading, isValidating, mutate } = useSWR(
    "/api/user",
    fetcher
  );

  if (isLoading) return <Spinner />;
  if (error) return <Error message={error.message} />;
  return <div>{data.name} {isValidating && <RefreshIcon />}</div>;
}
Enter fullscreen mode Exit fullscreen mode

SWR returns stale data instantly, then revalidates in the background.

Conditional Fetching

// Only fetch when userId exists
const { data } = useSWR(userId ? `/api/users/${userId}` : null, fetcher);

// Dependent fetching — wait for first request
const { data: user } = useSWR("/api/user", fetcher);
const { data: projects } = useSWR(
  user ? `/api/projects?userId=${user.id}` : null,
  fetcher
);
Enter fullscreen mode Exit fullscreen mode

Global Configuration

import { SWRConfig } from "swr";

function App() {
  return (
    <SWRConfig value={{
      fetcher: (url) => fetch(url).then(r => r.json()),
      refreshInterval: 30000,
      revalidateOnFocus: true,
      revalidateOnReconnect: true,
      dedupingInterval: 2000,
      errorRetryCount: 3,
      onError: (error) => Sentry.captureException(error),
    }}>
      <Dashboard />
    </SWRConfig>
  );
}
Enter fullscreen mode Exit fullscreen mode

Mutation: Optimistic Updates

const { data, mutate } = useSWR("/api/todos", fetcher);

async function addTodo(newTodo) {
  // Optimistically update UI
  await mutate(
    async (currentData) => {
      const res = await fetch("/api/todos", {
        method: "POST",
        body: JSON.stringify(newTodo)
      });
      const created = await res.json();
      return [...currentData, created];
    },
    {
      optimisticData: [...data, { ...newTodo, id: "temp" }],
      rollbackOnError: true,
      revalidate: false,
    }
  );
}
Enter fullscreen mode Exit fullscreen mode

Infinite Loading: useSWRInfinite

import useSWRInfinite from "swr/infinite";

const getKey = (pageIndex, previousPageData) => {
  if (previousPageData && !previousPageData.length) return null; // End
  return `/api/products?page=${pageIndex}&limit=20`;
};

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

  const products = data ? data.flat() : [];
  const isEnd = data && data[data.length - 1]?.length < 20;

  return (
    <>
      {products.map(p => <ProductCard key={p.id} product={p} />)}
      {!isEnd && (
        <button onClick={() => setSize(size + 1)} disabled={isValidating}>
          Load More
        </button>
      )}
    </>
  );
}
Enter fullscreen mode Exit fullscreen mode

Subscription: Real-Time Data

import useSWRSubscription from "swr/subscription";

function LivePrices({ symbol }) {
  const { data } = useSWRSubscription(`prices/${symbol}`, (key, { next }) => {
    const ws = new WebSocket(`wss://stream.example.com/${key}`);
    ws.onmessage = (e) => next(null, JSON.parse(e.data));
    ws.onerror = (e) => next(e);
    return () => ws.close();
  });

  return <span>${data?.price}</span>;
}
Enter fullscreen mode Exit fullscreen mode

Fetch scraped data with SWR? My Apify tools provide API endpoints perfect for SWR integration.

Custom data fetching solution? Email spinov001@gmail.com

Top comments (0)