Performance
SWR provides critical functionality in all kinds of web apps, so performance is a top priority.
SWR’s built-in caching and deduplication skips
unnecessary network requests, but the performance of the useSWR hook itself
still matters. In a complex app, there could be hundreds of useSWR calls in a
single page render.
SWR ensures that your app has:
- no unnecessary requests
- no unnecessary re-renders
- no unnecessary code imported
without any code changes from you.
Deduplication
It’s very common to reuse SWR hooks in your app. For example, an app that renders the current user’s avatar 5 times:
function useUser() {
return useSWR('/api/user', fetcher)
}
function Avatar() {
const { data, error } = useUser()
if (error) return <Error />
if (!data) return <Spinner />
return <img src={data.avatar_url} />
}
function App() {
return (
<>
<Avatar />
<Avatar />
<Avatar />
<Avatar />
<Avatar />
</>
)
}Each <Avatar> component has a useSWR hook inside. Since they have the same
SWR key and are rendered at the almost same time, only 1 network request will
be made.
You can reuse your data hooks (like useUser in the example above) everywhere,
without worrying about performance or duplicated requests.
There is also a dedupingInterval option for overriding the
default deduplication interval.
Deep Comparison
SWR deep compares data changes by default. If the data value isn’t
changed, a re-render will not be triggered.
You can also customize the comparison function via the
compare option if you want to change the behavior. For
example, some API responses return a server timestamp that you might want to
exclude from the data diff.
Dependency Collection
useSWR returns 3 stateful values: data, error and isValidating, each
one can be updated independently. For example, if we print those values within a
full data-fetching lifecycle, it will be something like this:
function App() {
const { data, error, isValidating } = useSWR('/api', fetcher)
console.log(data, error, isValidating)
return null
}In the worst case (the first request failed, then the retry was successful), you will see 4 lines of logs:
// console.log(data, error, isValidating)
undefined undefined true // => start fetching
undefined Error false // => end fetching, got an error
undefined Error true // => start retrying
Data undefined false // => end retrying, get the dataThe state changes make sense. But that also means our component rendered 4 times.
If we change our component to only use data:
function App() {
const { data } = useSWR('/api', fetcher)
console.log(data)
return null
}The magic happens — there are only 2 re-renders now:
// console.log(data)
undefined // => hydration / initial render
Data // => end retrying, get the dataThe exact same process has happened internally, there was an error from the
first request, then we got the data from the retry. However, SWR only updates
the states that are used by the component, which is only data now.
If you are not always using all these 3 states, you are already benefitting from this feature. At Vercel , this optimization results in ~60% fewer re-renders.
Tree Shaking
The SWR package is tree-shakeable
and side-effect free. That means if you are only importing the core useSWR
API, unused APIs like useSWRInfinite won’t be bundled in your application.