SWR is a React Hooks library for remote data fetching.
The name “SWR” is derived from stale-while-revalidate
, a HTTP cache invalidation strategy popularized by RFC 5861.
SWR first returns the data from cache (stale), then sends the fetch request (revalidate), and finally comes with the up-to-date data again.
It features:
- Transport and protocol agnostic data fetching
- Fast page navigation
- Revalidation on focus
- Interval polling
- Local mutation
- Pagination
- TypeScript ready
- Suspense mode
- React Native support
- Minimal API
...and a lot more.
With SWR, components will get a stream of data updates constantly and automatically. Thus, the UI will be always fast and reactive.
import useSWR from 'swr'
function Profile () {
const { data, error } = useSWR('/api/user', fetcher)
if (error) return <div>failed to load</div>
if (!data) return <div>loading...</div>
return <div>hello {data.name}!</div>
}
In this example, the React Hook useSWR
accepts a key
and a fetcher
function.
key
is a unique identifier of the request, normally the URL of the API. And the fetcher
accepts
key
as its parameter and returns the data asynchronously.
useSWR
also returns 2 values: data
and error
. When the request (fetcher) is not yet finished,
data
will be undefined
. And when we get a response, it sets data
and error
based on the result
of fetcher
and rerenders the component.
Note that fetcher
can be any asynchronous function, so you can use your favourite data-fetching
library to handle that part.
Check out swr.now.sh for more demos of SWR.
Inside your React project directory, run the following:
yarn add swr
Or with npm:
npm install swr
const { data, error, isValidating, revalidate } = useSWR(key, fetcher, options)
key
: a unique key string for the request (or a function / array / null) (advanced usage)fetcher
: (optional) a Promise returning function to fetch your data (details)options
: (optional) an object of options for this SWR hook
data
: data for the given key resolved byfetcher
(or undefined if not loaded)error
: error thrown byfetcher
(or undefined)isValidating
: if there's a request or revalidation loadingrevalidate
: function to trigger the validation manually
suspense = false
: enable React Suspense mode (details)fetcher = undefined
: the default fetcher functionrevalidateOnFocus = true
: auto revalidate when window gets focusedrefreshInterval = 0
: polling interval (disabled by default)refreshWhenHidden = false
: polling when the window is invisible (ifrefreshInterval
is enabled)shouldRetryOnError = true
: retry when fetcher has an error (details)dedupingInterval = 2000
: dedupe requests with the same key in this time spanfocusThrottleInterval = 5000
: only revalidate once during a time spanloadingTimeout = 3000
: timeout to trigger the onLoadingSlow eventerrorRetryInterval = 5000
: error retry interval (details)onLoadingSlow
: callback function when a request takes too long to load (loadingTimeout
)onSuccess
: callback function when a request finishs successfullyonError
: callback function when a request returns an erroronErrorRetry
: handler for error retryinitialData
: initial data to be returned (note: This is per-hook)
When under a slow network (2G, <= 70Kbps), errorRetryInterval
will be 10s, and
loadingTimeout
will be 5s by default.
You can also use global configuration to provide default options.
- Global Configuration
- Data Fetching
- Conditional Fetching
- Dependent Fetching
- Multiple Arguments
- Manually Revalidate
- Local Mutation
- Suspense Mode
- Error Retries
The context SWRConfig
can provide global configurations (options
) for all SWR hooks.
In this example, all SWRs will use the same fetcher provided to load JSON data, and refresh every 3 seconds by default:
import useSWR, { SWRConfig } from 'swr'
function Dashboard () {
const { data: events } = useSWR('/api/events')
const { data: projects } = useSWR('/api/projects')
const { data: user } = useSWR('/api/user', { refreshInterval: 0 }) // don't refresh
// ...
}
function App () {
return (
<SWRConfig
value={{
refreshInterval: 3000,
fetcher: (...args) => fetch(...args).then(res => res.json())
}}
>
<Dashboard />
</SWRConfig>
)
}
fetcher
is a function accepts the key
of SWR, and returns a value or a Promise.
You can use any library to handle data fetching, for example:
import fetch from 'unfetch'
const fetcher = url => fetch(url).then(r => r.json())
function App () {
const { data } = useSWR('/api/data', fetcher)
// ...
}
Or using GraphQL:
import { request } from 'graphql-request'
const API = 'https://api.graph.cool/simple/v1/movies'
const fetcher = query => request(API, query)
function App () {
const { data, error } = useSWR(
`{
Movie(title: "Inception") {
releaseDate
actors {
name
}
}
}`,
fetcher
)
// ...
}
If you want to pass variables to a GraphQL query, check out Multiple Arguments.
Note that fetcher
can be skipped from the parameters if it's provided gloablly.
Use null
or pass a function as the key
to useSWR
to conditionally fetch data. If the functions throws an error or returns a falsy value, SWR will cancel the request.
// conditionally fetch
const { data } = useSWR(shouldFetch ? '/api/data' : null, fetcher)
// ...or return a falsy value
const { data } = useSWR(() => shouldFetch ? '/api/data' : null, fetcher)
// ... or throw an error when user.id is not defined
const { data } = useSWR(() => '/api/data?uid=' + user.id, fetcher)
SWR also allows you to fetch data that depends on other data. It ensures the maximum possible parallelism (avoiding waterfalls), as well as serial fetching when a piece of dynamic data is required for the next data fetch to happen.
function MyProjects () {
const { data: user } = useSWR('/api/user')
const { data: projects } = useSWR(() => '/api/projects?uid=' + user.id)
// When passing a function, SWR will use the
// return value as `key`. If the function throws,
// SWR will know that some dependencies are not
// ready. In this case it is `user`.
if (!projects) return 'loading...'
return 'You have ' + projects.length + ' projects'
}
In some scenarios, it's useful pass multiple arguments (can be any value or object) to the fetcher
function. For example:
const token = props.token
useSWR('/api/data', url => fetchWithToken(url, token))
This is incorrect. Because the identifier of the data is '/api/data'
, which is also the index of the cache.
When token
changes, SWR will still treat it as the same key and request.
Instead, you can use an array as the key
parameter, which contains multiple arguments of fetcher
:
useSWR(['/api/data', token], fetchWithToken)
This solves the problem. The identifier of the request is now the combination of both values. SWR shallowly compares the arguments on every render, and triggers the validation if any of them has changed.
You can broadcast a revalidation message globally to all SWRs with the same key by calling
trigger(key)
.
This example shows how to automatically refetch the login info (e.g.: inside <Profile/>
)
when the user clicks the “Logout” button.
import useSWR, { trigger } from 'swr'
function App () {
return (
<div>
<Profile />
<button onClick={() => {
// set the cookie as expired
document.cookie = 'token=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/;'
// tell all SWRs with this key to revalidate
trigger('/api/user')
}}>
Logout
</button>
</div>
)
}
In many cases, applying local mutations to data is a good way to make changes feel faster — no need to wait for the remote source of data.
With mutate
, you can update your local data programmatically, while
revalidating and finally replace it with the latest data.
import useSWR, { mutate } from 'swr'
function Profile () {
const { data } = useSWR('/api/user', fetcher)
return (
<div>
<h1>My name is {data.name}.</h1>
<button onClick={async () => {
const newName = data.name.toUpperCase()
// send a request to the API to update the data
await requestUpdateUsername(newName)
// update the local data immediately and revalidate (refetch)
mutate('/api/user', { ...data, name: newName })
}}>Uppercase my name!</button>
</div>
)
}
You can enable the suspense
option to use SWR with React Suspense:
import { Suspense } from 'react'
import useSWR from 'swr'
function Profile () {
const { data } = useSWR('/api/user', fetcher, { suspense: true })
return <div>hello, {data.name}</div>
}
function App () {
return (
<Suspense fallback={<div>loading...</div>}>
<Profile/>
</Suspense>
)
}
Note in Suspense mode, data
is always the fetch response (so you don't need to check if it's undefined
). But if there's an error occurred, you need to use an error boundary to catch it.
By default, SWR uses the exponential backoff algorithm to handle error retries. You can read more from the source code.
It's also possible to override the behavior:
useSWR(key, fetcher, {
onErrorRetry: (error, key, option, revalidate, { retryCount }) => {
if (retryCount >= 10) return
if (error.status === 404) return
// retry after 5 seconds
setTimeout(() => revalidate({ retryCount: retryCount + 1 }), 5000)
}
})
- Shu Ding (@shuding_) – ZEIT
- Guillermo Rauch (@rauchg) – ZEIT
- Joe Haddad (@timer150) - ZEIT
- Paco Coursey (@pacocoursey) - ZEIT
Thanks to Ryan Chen for providing the awesome swr
npm package name!
The MIT License.