In today’s fast-paced digital world, users expect applications to feel responsive and up-to-date. Whether it’s a live dashboard, a social media feed, or a collaborative tool, real-time data fetching is no longer a nice-to-have—it’s a requirement. For React developers, achieving this can be tricky: managing network requests, caching, revalidation, and handling errors often leads to boilerplate code and complex state management.
Enter SWR (short for Stale-While-Revalidate), a lightweight React Hooks library developed by Vercel. SWR simplifies real-time data fetching by abstracting away the complexity of caching, revalidation, and state management. In this blog, we’ll explore how SWR works, its key features, and how to implement real-time data fetching in React applications using SWR.
Table of Contents
- Introduction to Real-Time Data Fetching
- What is SWR?
- Key Features of SWR
- Setting Up SWR in a React Project
- Basic Data Fetching with SWR
- Real-Time Updates with SWR
- Advanced SWR Features
- Performance Considerations
- SWR vs. React Query: A Quick Comparison
- Conclusion
- References
What is SWR?
SWR is a React Hooks library for data fetching developed by Vercel. The name “SWR” comes from the HTTP cache-control directive stale-while-revalidate, which allows a cache to serve stale data while asynchronously fetching fresh data in the background.
At its core, SWR provides a useSWR hook that takes a unique key (to identify the data) and a fetcher function (to fetch the data), then returns the current state of the request (data, error, loading status, etc.).
Key Features of SWR
SWR simplifies real-time data fetching with these built-in features:
- Stale-While-Revalidate: Serves cached data immediately and updates in the background.
- Automatic Revalidation: Refetches data when the window regains focus or the network reconnects.
- Polling: Configurable interval-based refetching for live data (e.g., dashboards).
- Caching: In-memory cache to avoid redundant network requests.
- Deduplication: Ensures multiple components fetching the same data don’t trigger duplicate requests.
- Error Handling: Automatic retries with configurable backoff strategies.
- Mutations: Manually update cached data and trigger revalidation.
- TypeScript Support: Full type safety for data and hooks.
Setting Up SWR in a React Project
Let’s set up SWR in a new or existing React project.
Step 1: Install SWR
Use npm or yarn to install SWR:
npm install swr
# or
yarn add swr
Step 2: Configure a Global Fetcher (Optional)
For consistency, define a reusable fetcher function to handle API requests. For example, using fetch:
// lib/fetcher.js
export const fetcher = async (url) => {
const res = await fetch(url);
// If the request failed, throw an error
if (!res.ok) {
const error = new Error('An error occurred while fetching data');
error.status = res.status;
throw error;
}
return res.json();
};
Step 3: Wrap Your App with SWRConfig (Optional)
To share global configuration (e.g., default fetcher, retry settings) across all useSWR hooks, wrap your app with SWRConfig:
// App.js
import { SWRConfig } from 'swr';
import { fetcher } from './lib/fetcher';
function App() {
return (
<SWRConfig
value={{
fetcher, // Default fetcher for all useSWR hooks
revalidateOnFocus: true, // Auto-revalidate when window is focused
dedupingInterval: 3000, // Deduplicate requests within 3 seconds
}}
>
<YourAppComponents />
</SWRConfig>
);
}
Basic Data Fetching with SWR
Let’s start with a simple example: fetching a list of posts from a REST API using useSWR.
Example: Fetching Posts
// components/Posts.js
import useSWR from 'swr';
function Posts() {
// Use SWR to fetch data from the API
const { data, error, isLoading } = useSWR('/api/posts', fetcher);
// Handle loading state
if (isLoading) return <div>Loading posts...</div>;
// Handle error state
if (error) return <div>Error: {error.message}</div>;
// Render data once loaded
return (
<ul>
{data.map((post) => (
<li key={post.id}>{post.title}</li>
))}
</ul>
);
}
export default Posts;
Key Return Values of useSWR
data: The fetched data (or cached data if available).error: An error object if the request failed.isLoading:trueif the request is in progress and no cached data exists.isValidating:trueif SWR is revalidating data in the background (e.g., fetching fresh data while showing stale data).
Real-Time Updates with SWR
SWR makes real-time data updates simple with built-in tools like polling, manual revalidation, and auto-revalidation on focus.
Polling for Continuous Updates
To fetch data at regular intervals (e.g., a live dashboard), use the refreshInterval option.
Example: Live Stock Prices
// components/StockPrice.js
import useSWR from 'swr';
import { fetcher } from '../lib/fetcher';
function StockPrice({ symbol }) {
const { data, error } = useSWR(
`/api/stocks/${symbol}`, // Unique key for the stock
fetcher,
{ refreshInterval: 5000 } // Refresh every 5 seconds (5000 ms)
);
if (error) return <div>Failed to load stock data</div>;
if (!data) return <div>Loading stock price...</div>;
return (
<div>
<h3>{symbol}: ${data.price}</h3>
<p>Last updated: {new Date(data.updatedAt).toLocaleTimeString()}</p>
</div>
);
}
Here, refreshInterval: 5000 ensures the stock price updates every 5 seconds.
Manual Revalidation with mutate
Sometimes you need to trigger revalidation manually (e.g., after submitting a form). Use SWR’s mutate function to update the cache and revalidate data.
Example: Adding a New Post
// components/AddPost.js
import useSWR, { mutate } from 'swr';
function AddPost() {
const { data: posts } = useSWR('/api/posts', fetcher);
const handleAddPost = async (newPost) => {
// 1. Send new post to the server
await fetch('/api/posts', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(newPost),
});
// 2. Trigger revalidation to fetch the updated list of posts
mutate('/api/posts'); // Revalidates the data for the key '/api/posts'
};
return (
<button onClick={() => handleAddPost({ title: 'New Post' })}>
Add Post
</button>
);
}
mutate('/api/posts') tells SWR to revalidate the data for the key /api/posts, ensuring the UI updates with the new post.
Revalidation on Focus
SWR automatically revalidates data when the window regains focus (enabled by default via revalidateOnFocus: true). This is useful for apps where data might change while the user was inactive (e.g., a messaging app).
To disable this behavior for a specific hook:
useSWR('/api/data', fetcher, { revalidateOnFocus: false });
Advanced SWR Features
Optimistic Updates
Optimistic updates improve UX by updating the UI before the server confirms the action. If the server request fails, SWR rolls back the cache to the previous state.
Example: Liking a Post Optimistically
// components/LikeButton.js
import useSWR, { mutate } from 'swr';
function LikeButton({ postId }) {
const { data: post } = useSWR(`/api/posts/${postId}`, fetcher);
const handleLike = async () => {
// 1. Optimistically update the cache
mutate(
`/api/posts/${postId}`,
{ ...post, likes: post.likes + 1 }, // Updated data
false // Don't revalidate yet (we'll do it after the request)
);
try {
// 2. Send request to the server
await fetch(`/api/posts/${postId}/like`, { method: 'POST' });
// 3. Revalidate to ensure cache matches server state
mutate(`/api/posts/${postId}`);
} catch (error) {
// 4. Rollback on error
mutate(`/api/posts/${postId}`, post, false);
alert('Failed to like post. Please try again.');
}
};
return (
<button onClick={handleLike}>
Like ({post?.likes || 0})
</button>
);
}
Dependent Fetching
Fetch data that depends on another request (e.g., fetching a user’s posts after fetching the user). Use a null key to pause fetching until the dependency is ready.
Example: Fetching User Posts
// components/UserPosts.js
import useSWR from 'swr';
function UserPosts({ userId }) {
// Fetch user data first
const { data: user } = useSWR(userId ? `/api/users/${userId}` : null, fetcher);
// Fetch posts only if user data is available
const { data: posts } = useSWR(
user ? `/api/users/${user.id}/posts` : null, // Dependent key
fetcher
);
if (!user) return <div>Loading user...</div>;
if (!posts) return <div>Loading posts...</div>;
return (
<div>
<h2>Posts by {user.name}</h2>
<ul>
{posts.map((post) => (
<li key={post.id}>{post.title}</li>
))}
</ul>
</div>
);
}
Error Handling and Retries
SWR automatically retries failed requests with exponential backoff. Customize retry behavior with options like retryCount and retryDelay:
useSWR('/api/data', fetcher, {
retryCount: 3, // Retry 3 times
retryDelay: (retryCount) => retryCount * 1000, // 1s, 2s, 3s delays
onError: (error) => console.error('Fetch failed:', error), // Custom error callback
});
Performance Considerations
- Caching: SWR caches data in memory, so repeated requests for the same key are served instantly.
- Deduplication: Multiple components fetching the same key trigger only one network request.
- Avoid Over-Rendering: Use
useSWR’sisValidatingto distinguish between initial loading and background revalidation:const { data, isLoading, isValidating } = useSWR('/api/data', fetcher); if (isLoading) return <Spinner />; // Show spinner only on initial load if (isValidating) return <div>Updating...</div>; // Show "updating" during revalidation
SWR vs. React Query: A Quick Comparison
Both SWR and React Query are popular for data fetching, but they differ in focus:
| Feature | SWR | React Query |
|---|---|---|
| Size | ~5KB (lightweight) | ~12KB (more features) |
| Philosophy | Stale-while-revalidate | Server state management |
| Advanced Features | Limited (mutate, polling) | Rich (query invalidation, pagination, infinite queries) |
| Learning Curve | Lower | Slightly steeper |
Use SWR if: You want a minimal, easy-to-learn library focused on real-time updates.
Use React Query if: You need advanced server state features (e.g., pagination, infinite scrolling).
Conclusion
SWR simplifies real-time data fetching in React by abstracting caching, revalidation, and error handling into a single hook. Its stale-while-revalidate pattern ensures fast UI updates, while features like polling, optimistic updates, and dependent fetching make it versatile for modern apps.
Whether you’re building a live dashboard, a social media feed, or a collaborative tool, SWR helps you deliver a responsive and up-to-date user experience with minimal code.