Open-source project
vercel/swr avatar
vercel/swr

vercel/swr: React Hooks for Data Fetching, and Where It Stops

GitHub describes it as React Hooks for Data Fetching. The repository metadata lists TypeScript as its primary language. The metadata lists the MIT license. This article stays within the project description and details documented in the GitHub repository README.

32,487 stars1,382 forksTypeScriptMIT

At a glance

What is it?
SWR is a React Hooks library built around the stale-while-revalidate cache strategy. It is small, transport agnostic, and shipped by the Next.js team. Here is how it installs, how the cache works, and when React Query is the better pick.
Who is it for?
Adopt SWR if you have a React or React Native app where most reads are keyed GETs and you want a small hook rather than a query client with a mutation lifecycle. Do not adopt it if your screen is mostly writes, if you need a persisted or normalized cache, or if you are not on React at all.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 8 days ago.
What is it written in?
Mainly TypeScript, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on September 22, 2026, and from our analysis. They are not legal advice.

Editorial analysis

What SWR actually replaces in a React app

The library exists to remove the useEffect plus useState pair that most React apps grow around every remote read. That pattern has three recurring bugs: a response arriving after unmount, two components firing the same request at the same time, and no way to refresh a value without a full page reload. SWR folds all three into one hook.

The name comes from stale-while-revalidate, a cache invalidation strategy the README attributes to HTTP RFC 5861. The README describes the sequence plainly: the hook first returns data from cache (stale), then sends the request (revalidate), then renders again with the fresh value. The audience is React and React Native developers who want that behaviour without wiring a store. The repository lists examples for pagination, optimistic UI, Suspense, server rendering and tab sync, which maps closely to the situations where hand-rolled fetching code tends to break.

The key plus fetcher contract, and the deduplication behind it

The whole API surface is two arguments. The first is a key, described in the README as a unique identifier of the request, normally the API URL. The second is a fetcher, any asynchronous function that accepts that key and returns the data. The README is explicit that the fetcher can come from any data-fetching library, which is why SWR is described as transport and protocol agnostic. Nothing in the core assumes HTTP.

The hook returns data, isLoading and error. While the fetcher has not resolved, data is undefined and isLoading is true; on resolution the hook sets data and error from the result, flips isLoading to false and rerenders. Because the key is the cache identity, two components calling useSWR with the same key share one entry and one in-flight request. That is the request deduplication the README lists, and it is the reason the key must be stable across renders.

The repository layout shows how optional behaviour is packaged. Alongside the root entry there are separate exports for ./infinite, ./immutable and ./subscription, plus a react-server condition in the exports map for React Server Components. Pagination, frozen caches and subscription-style updates are opt-in paths rather than weight every consumer carries.

Installing SWR and getting a first request on screen

The package is published to npm as swr, and the README's quick start imports the default export from it. A global fetcher is the pattern the repository's examples/global-fetcher directory is built around, and it is the one to copy first: define it once, then pass it to every hook call.

bash
npm install swr

With that installed, the README's Profile component is the smallest real use. It calls useSWR with a URL key and a fetcher, then branches on error and isLoading before reading data.name.

js
import useSWR from 'swr'

function Profile() {
  const { data, error, isLoading } = useSWR('/api/user', fetcher)

  if (error) return <div>failed to load</div>
  if (isLoading) return <div>loading...</div>
  return <div>hello {data.name}!</div>
}

What you should see: a loading message on first render, then the name. On a second mount of the same component with the same key, the cached value appears immediately while the request runs in the background. If you see "failed to load", the fetcher resolved with something the hook treated as an error, which usually means it returned a parsed error body instead of rejecting.

Where SWR is the wrong tool

The README's own framing gives away the boundary. It calls components the recipients of a stream of data updates, and the feature list is dominated by reads: revalidation on focus, revalidation on network recovery, polling, pagination, scroll position recovery, SSR and SSG. Mutations appear once, as local mutation for optimistic UI. There is no query invalidation graph, no mutation lifecycle with pending, success and error states, and no devtools mentioned anywhere in the README.

That matters for form-heavy or write-heavy screens. If your component posts, then needs to reconcile the server's response against several cached lists that the write affected, SWR leaves the reconciliation to you: you call mutate against the keys you know about. A library that models mutations as first-class objects does that bookkeeping for you. The same gap shows up with a normalized cache. SWR keys are flat, so an entity referenced by three different keys is three cache entries, and you keep them consistent by hand.

Two smaller constraints are worth flagging. The fetcher contract has no built-in rejection rule, so a fetcher that returns a response object without checking status will populate data with an error payload and never set error. And the key must be serializable and stable; an object literal rebuilt each render produces a new identity and defeats deduplication.

SWR vs React Query and TanStack Query

This is the comparison people search for, and the difference is scope rather than quality. SWR is a hook. React Query, now developed as TanStack Query, is a query client that you mount in a provider and then read from. SWR's README lists no provider requirement and no query client instance; the cache is implicit and keyed by whatever you pass as the first argument.

The practical consequence is where configuration lives. With SWR, options such as a refetch interval or a global fetcher are passed per call or through a context provider, and the repository's examples/global-fetcher and examples/refetch-interval directories show both shapes. With a query client, defaults are set once on the client and mutations, invalidation and cache persistence are part of the same object. If you want a persisted cache that survives a reload, or a mutation hook that invalidates related keys automatically, that is the dividing line, and SWR's README does not claim either.

The repository's own entry points hint at the intended weight. SWR ships ./infinite, ./immutable and ./subscription as separate subpath exports, so you only pay for the mode you import. A query client ships as one unit.

Maintenance, release cadence and the MIT licence

The repository is not archived, and the last push was on 2026-08-12, which is the same day v2.5.1 was released. v2.5.0 landed on 2026-08-03, after a v2.5.0-beta.1 on 2026-07-15. That is a release within roughly six weeks of the previous feature version, so the project is being maintained rather than frozen.

Upgrade cost is low by construction. The package sets sideEffects to false and ships dual ESM and CJS builds with types for both, so tree shaking and bundler resolution work without extra configuration. The exports map also carries a react-server condition for React Server Components, which means a version bump can change what a server component import resolves to even when your client code is untouched. That is the thing to check after upgrading.

The licence is MIT. In practical terms that permits commercial and closed-source use with the licence and copyright notice retained, but this is a description of the licence identifier in the repository, not legal advice for your situation.

Editorial conclusion

Adopt SWR if you have a React or React Native app where most reads are keyed GETs and you want a small hook rather than a query client with a mutation lifecycle. Do not adopt it if your screen is mostly writes, if you need a persisted or normalized cache, or if you are not on React at all. Before committing, verify three things in your own codebase: that your fetcher rejects on non-2xx responses, that you have a global fetcher or pass one to every call, and that your keys are stable strings rather than object literals rebuilt on each render.

Frequently asked questions

Which is better, SWR or React Query?

They solve overlapping problems at different scopes. SWR is a hook with a key and a fetcher, and its README lists reads, caching, deduplication and revalidation. React Query, now TanStack Query, is a query client with a mutation lifecycle and invalidation graph, which SWR's README does not describe. Pick SWR for keyed reads and React Query when writes drive cache updates.

What is Vercel SWR?

It is a React Hooks library for data fetching, created by the team behind Next.js. The name comes from stale-while-revalidate, and the README describes the flow as returning cached data first, then revalidating, then rendering the fresh value.

How do I install SWR in a React project?

Install the npm package swr, then import the default export and call useSWR with a key such as an API URL and a fetcher function. The README's quick start shows a Profile component that reads data, error and isLoading from the hook.

Does SWR work with React Native?

Yes. React Native appears in the README's feature list, and because the fetcher is any asynchronous function and the library is described as transport agnostic, the hook itself does not depend on browser APIs.

Why does my SWR fetcher not set the error value?

The hook sets error from the result of the fetcher, and the README does not describe any built-in status check. If your fetcher resolves with a parsed error body instead of rejecting, that body becomes data. Make the fetcher throw on a non-2xx response.

Official sources

  1. Official documentation
  2. Official README
  3. Project repository
  4. Release notes
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/vercel-swr.svg)](https://hysenlabs.com/projects/vercel-swr)