SWR is a hooks library that provides React Hooks for remote data fetching. As the SWR docs state, the name “SWR” is derived from stale-while-revalidate
, which is an HTTP cache invalidation strategy.
In a nutshell, the stale-while-revalidate
strategy simply means serving or returning the stale (cached) data while you revalidate (fetch) the updated data and serve the updated data to the UI
once it is returned. The revalidation of stale data occurs under the hood on user focus or network reconnect.
The result of this is that our application components get a stream of fresh data constantly, making our UI
blazing fast, very reactive, and more responsive.
Another awesome feature we get out of the box that redounds to our application performance is deduplication. Deduplication is a technique to eliminate redundant data, and by implementing this technique, SWR does intelligent fetching.
What this is means is that when we render a component that reuses the useSWR
hook multiple times, SWR would prevent multiple uses of the useSWR
hook to revalidate data if the keys are the same, consequently eliminating unnecessary network requests because only one request would be made and stale data would be served to subsequent requests.
In this article, we’ll be looking at the awesome additions to SWR version 1, but if you are new to SWR, you can get started by reading our previous useSWR article.
SWR version 1 provides a significantly smaller library and improved performance features like tree shaking. According to the documentation, version 1 is significantly smaller — 41 percent smaller core or 24 percent smaller when gzipped.
The SWR package is tree-shakable because performance is one of the most important features of SWR.
Version 1 supports path imports as seen below:
import useSWR from 'swr' import useSWRInfinite from 'swr/infinite'
With path imports, in cases where we are not using the useSWRInfinite
hook, it would not be bundled in our application. Removing dead code makes our final bundle size significantly smaller.
By making the library more tree-shakable and lightweight, our application will have a smaller bundle, a leaner runtime, and a smaller node_modules
directory.
By default, SWR uses a global cache. However, version 1 takes this further by providing the provider API. The provider API consists of a cache provider that is a map-like object that matches the following TypeScript definition:
interface Cache<Data> { get(key: string): Data | undefined set(key: string, value: Data): void delete(key: string): void }
The cache provider is passed to the provider option of the SWRConfig
as seen below:
import useSWR, { SWRConfig } from 'swr' function App() { return ( <SWRConfig value={{ provider: () => new Map() }}> <Blog/> </SWRConfig> ) }
The result of this is that our custom cache provider will be used by all the SWR hooks within SWRConfig
. In other words, all the SWR hooks inside Blog
will read and write from that map
instance, but the SWR hooks that are not nested within the cache provider fall back to the default cache, which is an empty map
. Because of this, cache providers should be placed higher in the component tree or outside render.
Cache providers are intended to adapt SWR to support more storage options, and they can be a JavaScript Map instance or other cache provider implementation depending on our use case.
SWR v1 provides experimental support for extending the current cache provider. Consequently, we can sync the cache provider with the local storage, reset the cache between test cases, and more with the extended cache provider.
Also, in version 1 we can get access to the current cache provider when inside a React component by using the useSWRConfig
hook as seen below:
import { useSWRConfig } from 'swr' const Post() { const { cache, ...extraConfig } = useSWRConfig() cache.get(key) // Get the current data for the specified key. cache.clear() // Clear all the cache. SWR will revalidate upon re-render. // ... }
N.B., writing to the cache directly might cause undefined behaviors of SWR. The recommended practice is to use SWR APIs such as mutation.
useSWRConfig()
While the SWRConfig
context provides global configuration options for all SWR hooks, the new useSWRConfig()
hook enables us to access these options when inside a React component.
Consider the code below:
import { useSWRConfig } from 'swr' function MyComponent () { const { refreshInterval, mutate, ...restConfig } = useSWRConfig() // ... do something }
From our example on caching, we saw that we can also access the cache using useSWRConfig
.
Out of the box, SWR supports automatic revalidation on the following events:
This revalidation occurs when we refocus a page or switch between tabs. This helps to provide fresh data to our app. By default, this feature is turned on, but you can disable it via the revalidateOnFocus
option.
In many cases, our app’s data can become stale quickly because of many devices, multiple users, and multiple open tabs. This feature enables SWR to perform polling on interval, and this provides a way to synchronize to the latest state and update the data on the UI over time.
You can enable this by setting the refreshInterval
option with the value in the fetcher
function:
useSWR('/api/todos', fetcher, { refreshInterval: 2000 })
The above code simply means polling would only occur every 2000ms.
Also, SWR enables us to perform polling when the window is invisible — if refreshInterval
is enabled — by using the refreshWhenHidden
option, and when the browser is offline using the refreshWhenOffline
option.
This revalidation occurs when networks recover, e.g., in a scenario when the user is back online. It can be configured using the revalidateOnReconnect
option.
In version 1, SWR provides a helper, the useSWRImmutable
hook, to make state immutable. This means the state does not change when we revalidate.
The useSWRImmutable
hook is similar to the useSWR
hook and we can make state immutable using both as seen below:
import useSWR from 'swr' import useSWRImmutable from 'swr/immutable' useSWRImmutable(key, fetcher, options) // makes resourse immutable useSWR(key, fetcher, { revalidateIfStale: false, revalidateOnFocus: false, revalidateOnReconnect: false }) // equivalent to useSWRImmutable(key, fetcher) // makes resourse immutable
In the code above, the two hooks make the data immutable once it is cached. useSWRImmutable
does it automatically for us.
The SWR middleware API provides a great way to abstract and reuse logic.
The middleware is a closure that receives the SWR hook as an argument and wraps around the useSWR
hook, thereby enabling us to execute logic before and after invoking the useSWR
hooks.
Consider the code below:
function SWRMiddleware (useSWRNext) { return (key, fetcher, config) => { // beform operations before invoking useSWR() // Handle the next middleware, or invokes `useSWR` hook if this is the last middleware. const swr = useSWRNext(key, fetcher, config) // befrom operations after invoking useSWR() return swr } }
The SWR middleware is very useful and powerful, and a lot of ideas can be implemented using this feature. An example from the documentation is a request logger.
The logger middleware is a function decorator that decorates the fetcher
function with the ability to log requests — the SWR key in this case:
function logger(useSWRNext) { return (key, fetcher, config) => { // Add logger to the original fetcher. const extendedFetcher = (...args) => { console.log('SWR Request:', key) return fetcher(...args) } // Execute the hook with the new fetcher. return useSWRNext(key, extendedFetcher, config) } }
Consequently, when we use this middleware, the SWR key is logged to the console:
... // ... used from inside your component useSWR(key, fetcher, { use: [logger] }) SWR Request: /api/user1 SWR Request: /api/user2 ...
To use this feature, we pass an array of middlewares to either SWRConfig
or useSWR
:
<SWRConfig value={{ use: [middlewareA, middleWareB, middlewareC] }}> // or... useSWR(key, fetcher, { use: [middlewareA, middleWareB, middlewareC] })
When dealing with multiple middlewares, each middleware is nested within the other and the last middleware receives the SWR hook.
This new feature simply enables us to provide arbitrary prefetched data for all SWR with specific keys:
<SWRConfig value={{ fallback: { '/user': { name: 'Eagles', ... }, '/posts': ..., '/pages': ..., ... } }}> <App/> </SWRConfig>
These prefetched data can be displayed as placeholder content on your page while your application revalidates. This improves the user experience and it is very helpful in scenarios such as static site generation (SSG) and server-side rendering (SSR).
Also, for consistency’s sake and to avoid confusion, the old initialData
has been renamed to fallbackData
but it still provides a single fallback value for the specified SWR hook.
SWR is an awesome, well-thought-out, lightweight, and fast data fetching library. It is transport- and protocol-agnostic — it supports both REST and GraphQL, it supports suspense
, and it is TypeScript-ready. The patterns implemented in SWR make developers’ lives easier.
And SWR version 1 is definitely a must-use for every developer. You can learn more about version 1 here. You can also read the changelog for more information.
Install LogRocket via npm or script tag. LogRocket.init()
must be called client-side, not
server-side
$ npm i --save logrocket // Code: import LogRocket from 'logrocket'; LogRocket.init('app/id');
// Add to your HTML: <script src="https://cdn.lr-ingest.com/LogRocket.min.js"></script> <script>window.LogRocket && window.LogRocket.init('app/id');</script>
Would you be interested in joining LogRocket's developer community?
Join LogRocket’s Content Advisory Board. You’ll help inform the type of content we create and get access to exclusive meetups, social accreditation, and swag.
Sign up nowCompare Prisma and Drizzle ORMs to learn their differences, strengths, and weaknesses for data access and migrations.
It’s easy for devs to default to JavaScript to fix every problem. Let’s use the RoLP to find simpler alternatives with HTML and CSS.
Learn how to manage memory leaks in Rust, avoid unsafe behavior, and use tools like weak references to ensure efficient programs.
Bypass anti-bot measures in Node.js with curl-impersonate. Learn how it mimics browsers to overcome bot detection for web scraping.