NewYour coding agent can read the release notes before it upgrades.Set up the MCP server →
npm · #2622 most downloaded on npm
React Hooks library for remote data fetching
Last release 11 days ago
22 Sep 2026
Release timing varies
gaps range from 9 days to 4 months
Nearly every release is documented
notes for 57 of the last 60 stable releases
1 version withdrawn
withdrawn after publishing
8 years old
181 releases · first in 2018
This type was already marked as deprecated in 1.x, and it now removed in this beta. If you are using it, please do the following change:
SWR 2.0 on its way! Check https://github.com/vercel/swr/releases/tag/2.0.0-beta.0 for the previous 2.0 beta updates.
💖 Give feedback in discussion: https://github.com/vercel/swr/discussions/1932.
isLoading state (#1928)Previously, useSWR only returns a isValidating state, which is an indicator of both initial requests and automatic & manual revalidations. It includes polling requests and focus revalidations, etc.
But if you need to display an initial skeleton while loading the data, you will have to do something like
const isLoading = typeof data === 'undefined' && !error
...which is a popular pattern in the community. In this case, isValidating doesn't help much.
In this release, useSWR, useSWRInfinite and useSWRImmutable will return an extra isLoading state along with the isValidating state. They will fit to different scenarios:
function Stock() {
const { data, isLoading, isValidating } = useSWR(STOCK_API, fetcher, {
refreshInterval: 3000
});
// If it's still loading the initial data, there is nothing to display.
// We return a skeleton here.
if (isLoading) return <div className="skeleton" />;
// Otherwise, display the data and a spinner that indicates a background
// revalidation.
return (
<>
<div>AAPL ${data}</div>
{isValidating ? <div className="spinner" /> : null}
</>
);
}
In the example above, we display a skeleton while loading the data. After the data is loaded, we show a spinner next to the data whenever we are re-fetching (revalidating):
You can find the full code for this example here: https://codesandbox.io/s/swr-isloading-v8dfpy.
keepPreviousData option (#1929)When doing data fetching based on continuous user actions, e.g. real-time search when typing, keeping the previous fetched data can improve the UX a lot.
In SWR 2.0, there is now a keepPreviousData option to enable that behavior. Here's a simple search UI:
function Search() {
const [search, setSearch] = React.useState('');
const { data, isLoading } = useSWR(`/search?q=${search}`, fetcher, {
keepPreviousData: true
});
return (
<div>
<input
type="text"
value={search}
onChange={(e) => setSearch(e.target.value)}
placeholder="Search..."
/>
<div className={isLoading ? "loading" : ""}>
{data?.products.map(item => <Product key={item.id} name={item.name} />)
</div>
</div>
);
}
With keepPreviousData enabled, you will still get the previous data even if you change the SWR key and the data for the new key starts loading again. This improves the visual continuity quite a lot, the search feels smoother after flipping the switch:
https://user-images.githubusercontent.com/3676859/163695903-a3eb1259-180e-41e0-821e-21c320201194.mp4
You can find the full code for this example here: https://codesandbox.io/s/swr-keeppreviousdata-fsjz3m.
InfiniteFetcher is renamed to SWRInfiniteFetcher (#1930)This type was already marked as deprecated in 1.x, and it now removed in this beta. If you are using it, please do the following change:
- import { InfiniteFetcher } from 'swr/infinite'
+ import { SWRInfiniteFetcher } from 'swr/infinite'
isLoading state and refactor the core by @shuding in https://github.com/vercel/swr/pull/1928keepPreviousData option by @shuding in https://github.com/vercel/swr/pull/1929Full Changelog: https://github.com/vercel/swr/compare/2.0.0-beta.0...2.0.0-beta.1
One column per quarter.
SWR 2.0 coming soon, and this is the first beta version!
SWR 2.0 coming soon, and this is the first beta version!
Keep in mind that APIs might still change until the stable release. Documentation will also be updated once stable.
💖 Give feedback in discussion: https://github.com/vercel/swr/discussions/1919.
Added in #1450, the new useSWRMutation hook covers all the use cases of:
isValidating but for mutations.Here's a quick example of how it looks:
import useSWRMutation from 'swr/mutation'
async function sendRequest(url, { arg }) {
return fetch(url, {
method: 'POST',
body: JSON.stringify(arg)
})
}
function App() {
const { trigger } = useSWRMutation('/api/user', sendRequest)
return <button onClick={() => {
trigger({ username: 'johndoe' })
}}>Create User</button>
}
In this example, the "fetcher", which is sendRequest, will receive the value { username: 'johndoe' } as the arg from the second parameter. The request will only be triggered when clicking the button.
The new useSWRMutation hook is actually more powerful than this, it also supports:
useSWRuseSWR after mutation finishesMore examples to come.
Previously, if the key is an array, the values will be passed to the fetcher function as arguments separately. In 2.0, the key will always be passed to the fetcher as is.
Before:
// SWR 1.x
useSWR([1, 2, 3], (a, b, c) => {
assert(a === 1)
assert(b === 2)
assert(c === 3)
})
After 2.0.0:
// SWR 2.0.0
useSWR([1, 2, 3], (a) => {
assert(a === [1, 2, 3])
})
This change affects the code that directly reads/writes to the cache, or provides a cache preset. For example if you have something like cache.set(key, value), you'll have to update your code.
Previously, the cached value of key will be the associated data, so this was guaranteed:
// SWR 1.x
assert(cache.get(key) === data)
And we keep other states (error, isValidating) with a special, prefixed key. Something like '$err$' + key.
Since 2.0.0, the internal structure will be an object that holds all the current states:
// SWR 2.0.0
assert(cache.get(key) === { data, error, isValidating })
So you will have to do the following change to your code, get:
- cache.get(key)
+ cache.get(key)?.data
And set:
- cache.set(key, data)
+ cache.set(key, { ...cache.get(key), data })
Full Changelog: https://github.com/vercel/swr/compare/1.2.2...2.0.0-beta.0
type: fix type error on SWRConfig by @Himself65 in #1913
Full Changelog: https://github.com/vercel/swr/compare/1.2.2...1.3.0
We added better Optimistic UI support in v1.2.0. However, what if your API is only returning a subset of the data (such as the mutated part), that can
populateCache Option Now Supports FunctionWe added better Optimistic UI support in v1.2.0. However, what if your API is only returning a subset of the data (such as the mutated part), that can be populated into the cache? Usually, an extra revalidation after that mutation is needed. But now you can also use a function as populateCache to transform the mutate result into the full data:
await mutate(addTodo(newTodo), {
optimisticData: [...data, newTodo],
rollbackOnError: true,
populateCache: (addedTodo, currentData) => {
// `addedTodo` is what the API returns. It's not
// returning a list of all current todos but only
// the new added one.
// In this case, we can transform the mutate result
// together with current data, into the new data
// that can be updated.
return [...currentData, addedTodo];
},
// Since the API already gives us the updated information,
// we don't need to revalidate here.
revalidate: false,
});
The new definition:
populateCache?: boolean | ((mutationResult: any, currentData: Data) => Data)
Here is a demo for it: https://codesandbox.io/s/swr-basic-forked-hi9svh
populateCache as a function by @shuding in https://github.com/vercel/swr/pull/1818Full Changelog: https://github.com/vercel/swr/compare/1.2.1...1.2.2
Previously `shouldRetryOnError` is either true or false. Now it accepts a function that conditionally determines if SWR should retry. Here's a simple
shouldRetryOnError accepts a functionPreviously shouldRetryOnError is either true or false. Now it accepts a function that conditionally determines if SWR should retry. Here's a simple example:
const fetcher = url => fetch(url).then(res => {
// Fetcher throws if the response code is not 2xx.
if (!res.ok) throw res
return res.json()
})
useSWR(key, fetcher, {
shouldRetryOnError: (error) => {
// We skip retrying if the API is returning 404:
if (error.status === 404) return false
return true
}
})
Thanks to @sairajchouhan for contributing!
shouldRetryOnError accepts a function that can be used to conditionally stop retrying by @sairajchouhan in https://github.com/vercel/swr/pull/1816Full Changelog: https://github.com/vercel/swr/compare/1.2.0...1.2.1
Nothing published for this version
There are now some new options in mutate:
There are now some new options in mutate:
mutate(patchUser(user), {
optimisticData: user,
populateCache: true,
rollbackOnError: true,
revalidate: true,
})
Here the cache will be immediately updated to user, the “optimistic value”. And then a request (remote mutation) is started via patchUser(user) and the response will be written to the cache. If that request fails, the original result will be rolled back safely so the optimistic value will be gone. And after all those finish, a revalidation will start to fetch the latest value.
This is extremely helpful for building the optimistic UI pattern.
You can do the same for the global mutate, just remember to pass the key. Also, the current mutate APIs stay unchanged so mutate(data, false) works the same.
Here's an example: https://codesandbox.io/s/swr-basic-forked-k5hps.
https://user-images.githubusercontent.com/3676859/151381238-f3aba769-91b6-4bfc-9285-df1b9ef51773.mp4
.mjs SupportSWR now has .mjs exported for bundlers that prefer this format.
This doesn’t break environments that don’t support .mjs. An alternative .esm.js and CJS bundle are also published.
You can read more about ES modules here.
Full Changelog: https://github.com/vercel/swr/compare/1.1.2...1.2.0
doc: comment helper by @huozhi in https://github.com/vercel/swr/pull/1757
Full Changelog: https://github.com/vercel/swr/compare/1.1.2...1.2.0-beta.1
There are now some new options in mutate:
There are now some new options in mutate:
mutate(patchUser(user), {
optimisticData: user,
populateCache: true,
rollbackOnError: true,
revalidate: true,
})
Here the cache will be immediately updated to user, the “optimistic value”. And then a request (remote mutation) is started via patchUser(user) and the response will be written to the cache. If that request fails, the original result will be rolled back safely so the optimistic value will be gone. And after all those finish, a revalidation will start to fetch the latest value.
This is extremely helpful for building the optimistic UI pattern.
You can do the same for the global mutate, just remember to pass the key. Also, the current mutate APIs stay unchanged so mutate(data, false) works the same.
Here's an example: https://codesandbox.io/s/swr-basic-forked-k5hps.
Full Changelog: https://github.com/vercel/swr/compare/1.1.2...1.2.0-beta.0
SWR will now use the latest fetcher function passed to the hook, when sending the request. Previously it uses the initial snapshotted fetcher.
SWR will now use the latest fetcher function passed to the hook, when sending the request. Previously it uses the initial snapshotted fetcher.
When refocusing on the window with state changes (like clicking a button that changes the SWR key immediately), SWR now avoids revalidations if they're not necessary. Details can be found in #1720.
useSWRInfiniteTwo types for useSWRInfinite are added: SWRInfinteHook and SWRInfinteKeyLoader. You can use them to type the hook and the getKey function.
populateCache option for mutateA new option to control if the mutation data should be written to the cache. You can find the details in #1729.
requestAnimationFrame more robust by @thomaspaulmann in https://github.com/vercel/swr/pull/1707populateCache option to mutate by @shuding in https://github.com/vercel/swr/pull/1729Full Changelog: https://github.com/vercel/swr/compare/1.1.1...1.1.2
We now have 23 test suits of 202 total test cases, with a 98.06% test coverage. The core files and main functionalities are now 100% covered. Kudos to
We now have 23 test suits of 202 total test cases, with a 98.06% test coverage. The core files and main functionalities are now 100% covered. Kudos to @promer94 for improving it!
SWR will now use the latest fetcher function passed to the hook, when sending the request. Previously it uses the initial snapshotted fetcher.
When refocusing on the window with state changes (like clicking a button that changes the SWR key immediately), SWR now avoids revalidations if they're not necessary. Details can be found in #1720.
useSWRInfiniteTwo types for useSWRInfinite are added: SWRInfinteHook and SWRInfinteKeyLoader. You can use them to type the hook and the getKey function.
Full Changelog: https://github.com/vercel/swr/compare/1.1.2-beta.0...1.1.2-beta.1
Make polyfill for requestAnimationFrame more robust by @thomaspaulmann in https://github.com/vercel/swr/pull/1707
requestAnimationFrame more robust by @thomaspaulmann in https://github.com/vercel/swr/pull/1707Full Changelog: https://github.com/vercel/swr/compare/1.1.1...1.1.2-beta.0
You can now pass a function as refreshInterval, to dynamically return the interval (in millisecond) til the next request, based on the current data:
refreshIntervalYou can now pass a function as refreshInterval, to dynamically return the interval (in millisecond) til the next request, based on the current data:
useSWR('key', fetcher, {
refreshInterval: function (data: Data | undefined) {
if (!data) return 3000 // Initial request
return data.next_update
}
})
If return 0, polling will be stopped.
newData is deeply to the latest state, broadcast the latest state by @icyJoseph in https://github.com/vercel/swr/pull/1697undefined data if key's falsy under suspense mode by @icyJoseph in https://github.com/vercel/swr/pull/1698Full Changelog: https://github.com/vercel/swr/compare/1.1.0...1.1.1
``js useSWR({ query: graphql..., variables }, fetcher) useSWR({ query: graphql..., variables }, fetcher) useSWR({ variables, query: graphql... }, fetc
useSWR({ query: graphql`...`, variables }, fetcher)
useSWR({ query: graphql`...`, variables }, fetcher)
useSWR({ variables, query: graphql`...` }, fetcher)
// ^all recognized as the same resource
For array keys too, it's safe to do the following:
useSWR([ `...`, { variables } ], fetcher)
revalidateFirstPage for useSWRInfiniteThis is a new added option for useSWRInfinite (defaults to true), you can use it to control if the first page should be revalidated when changing the size or doing a mutation.
axios with fetch interceptor by @danestves in https://github.com/vercel/swr/pull/1548type: "module" and use .mjs extension by @shuding in https://github.com/vercel/swr/pull/1604getKey in useSWRInfinite by @shuding in https://github.com/vercel/swr/pull/1681Full Changelog: https://github.com/vercel/swr/compare/1.0.1...1.1.0
Chore: rename major branch to main: #1664
Huge thanks to @huozhi, @promer94, and @TakahiroHimi for helping!
Fix: supports ssr in ie11(#1657): #1659
Huge thanks to @Sh031224 and @promer94 for helping!
Build: output without minification: #1650
Huge thanks to @promer94, @huozhi, and @nguyenyou for helping!
Revert "chore: Add type: "module" and use .mjs extension" (#1618): #1604
type: "module" and use .mjs extension" (#1618): #1604Huge thanks to @huozhi for helping!
Chore: Add type: "module" and use .mjs extension: #1604
type: "module" and use .mjs extension: #1604Fix: Support for functions that return false: #1586
Huge thanks to @TakahiroHimi, @morgs32, @aFINKndreas, @zhaomengfan, and @junsushin-dev for helping!
Chore: Refactor cleanupState: #1544
axios with fetch interceptor: #1548Huge thanks to @danestves and @huozhi for helping!
Mark data and error as readonly: #1514
Huge thanks to @timas130 and @huozhi for helping!
Feat: Add new onDiscarded option: #1523
Fix: Invalidate the key when mutating without revalidating: #1498
Huge thanks to @huozhi and @promer94 for helping!
Ci: multiple dir type checking: #1492
Huge thanks to @promer94 and @huozhi for helping!
Add react 18 to peerDependencies: #1467
Huge thanks to @huozhi and @koba04 for helping!
Should treat key change as remount: #1430
Huge thanks to @huozhi, @koba04, @anirudh1713, @anothertempore, and @amannn for helping!
https://github.com/vercel/swr/releases/tag/1.0.1-beta.0
Should treat key change as remount: #1430
Huge thanks to @koba04 and @huozhi for helping!
Feat: add a warning for invalid arguments with suspense mode: #1402
useSWRConfig is exported as () => any type: #1409Huge thanks to @louisgv, @willheslam, @koba04, @huozhi, and @origin-master for helping!
See full changes of this major & previous beta updates on the Releases page. Also read our blog post and migration guide: https://swr.vercel.app/blog/
See full changes of this major & previous beta updates on the Releases page. Also read our blog post and migration guide: https://swr.vercel.app/blog/swr-v1.
Huge thanks to @koba04 for helping!
Doc: update fallbackData usage examples: #1380
Huge thanks to @huozhi and @karaggeorge for helping!
Chore: add test for callback setSize: #1373
Huge thanks to @huozhi for helping!
New useSWRConfig API and remove createCache: #1361
Huge thanks to @huozhi and @pacocoursey for helping!
Fix: isValidating should be false when no fetcher is provided: #1352
Huge thanks to @huozhi for helping!
Export unstable_serialize from SWR: #1337
unstable_serialize from SWR: #1337useSWRInfinite: #1343Huge thanks to @huozhi for helping!
BREAKING: Deprecate revalidate with mutate: #1332
revalidate with mutate: #1332useSWRInfinite reuses useSWR cache: #1341Huge thanks to @huozhi, @heysailor, @koba04, and @eric-hc for helping!
Move infinite types to swr/infinite and deprecate old ones: #1272
Huge thanks to @huozhi and @koba04 for helping!
Sub-packages and immutable hook: #992
Huge thanks to @javascripter, @koba04, @huozhi, and @pa-rang for helping!
Replace deprecated types with latest types in axios-typescript example: #1128
null cache in useSWRInfinite: #1110Huge thanks to @huozhi, @koba04, @promer94, @nstepien, @MoonBall, @mkrtchian, @eunsukimme, @Tak-Iwamoto, @meeoh, and @teinett for helping!
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Huge thanks to @promer94 for helping!
Huge thanks to @promer94 for helping!
Nothing published for this version
Refactor: fix a react-hooks/exhaustive-deps warning: #1035
Huge thanks to @koba04, @jakubriedl, and @huozhi for helping!
Test: refactor tests for configs: #1006
Huge thanks to @promer94 and @koba04 for helping!
Nothing published for this version
Fix: revalidate with initialData when changing the key: #961
Huge thanks to @koba04 and @huozhi for helping!
Types: makes types fn and config and getKey more strictly: #946
Huge thanks to @koba04, @huozhi, @Kexin-Li, @promer94, @dingran, and @ramyareye for helping!
Nothing published for this version
Nothing published for this version
Nothing published for this version
Chore: add comments that revalidate will be deprecated: #937
Huge thanks to @koba04 for helping!
Enhance: let config.isPaused works with react state: #871
Huge thanks to @huozhi, @koba04, @mcamis, @joshuakimDwan, @joakimgrr, and @promer94 for helping!
Nothing published for this version
Your coding agent can read these notes before it upgrades. Set up the MCP server →