How to Optimize SWR in React
This article provides a practical guide on how to optimize the SWR (Stale-While-Revalidate) data-fetching library in React applications. You will learn how to fine-tune revalidation settings, leverage optimistic updates, implement request deduplication, and prefetch data to significantly improve your application’s performance and user experience.
1. Adjust Default Revalidation Settings
By default, SWR aggressively revalidates data to ensure it is always
up to date. However, features like revalidating on window focus or
network reconnection can cause unnecessary API requests. You can disable
or customize these settings globally using SWRConfig or
locally inside individual useSWR hooks.
// Disable automatic revalidation on focus and reconnect
const { data } = useSWR('/api/user', fetcher, {
revalidateOnFocus: false,
revalidateOnReconnect: false,
refreshInterval: 0, // Disable polling unless explicitly needed
});2. Leverage Deduping Intervals
If multiple components on the same page request the same API endpoint
simultaneously, SWR automatically dedupes these requests. By default,
the dedupingInterval is set to 2000ms (2 seconds). If your
data does not change frequently, you can increase this interval to
prevent redundant network requests.
const { data } = useSWR('/api/static-data', fetcher, {
dedupingInterval: 60000, // Dedupes requests for 1 minute
});3. Enable
keepPreviousData for Pagination and Filtering
When implementing pagination, search, or filtering, updating the SWR
key usually triggers a loading state, causing the UI to flicker.
Enabling keepPreviousData keeps the old data on the screen
while the new data is being fetched, ensuring a seamless user
transition.
const [page, setPage] = useState(1);
const { data } = useSWR(`/api/items?page=${page}`, fetcher, {
keepPreviousData: true,
});4. Implement Optimistic Updates with Mutate
Instead of waiting for a server response to update the UI after a
mutation (like adding a comment or liking a post), you can use SWR’s
mutate function to perform optimistic updates. This
immediately updates the local cache and UI, then validates the change
with the server in the background.
const { mutate } = useSWRConfig();
const handleAddTodo = async (newTodo) => {
const options = {
optimisticData: [...data, newTodo],
rollbackOnError: true,
populateCache: true,
revalidate: false,
};
await mutate('/api/todos', updateTodoOnServer(newTodo), options);
};5. Prefetch Data Before Rendering
To eliminate loading times entirely, you can prefetch data before a
component renders. SWR provides a preload API that
initiates the fetch request early in the application lifecycle, such as
when a user hovers over a link or button.
import { preload } from 'swr';
// Trigger preloading when a user hovers over a navigation link
const prefetchUserData = () => {
preload('/api/user', fetcher);
};
return (
<button onMouseEnter={prefetchUserData} onClick={navigateToProfile}>
View Profile
</button>
);6. Use a Custom Cache Provider for Persistence
SWR stores its cache in memory by default, meaning data is lost upon
a page refresh. You can optimize load times on subsequent visits by
syncing SWR’s cache with localStorage or
sessionStorage using a custom cache provider.
function localStorageProvider() {
const map = new Map(JSON.parse(localStorage.getItem('app-cache') || '[]'));
window.addEventListener('beforeunload', () => {
const appCache = JSON.stringify(Array.from(map.entries()));
localStorage.setItem('app-cache', appCache);
});
return map;
}
// Wrap your app with SWRConfig
<SWRConfig value={{ provider: localStorageProvider }}>
<App />
</SWRConfig>