Ever wonder how to keep static pages fresh without sacrificing performance? Traditional static sites struggle with dynamic data, but Next.js Incremental Static Regeneration (ISR) combined with revalidateTag offers a powerful solution for near real-time updates. This guide walks you through implementing efficient cache invalidation and client-side updates for a seamless user experience.
Understanding ISR and revalidateTag for Dynamic Data
Next.js ISR allows static pages to be regenerated on-demand after initial build, striking a balance between static performance and dynamic content. Unlike full SSR, ISR caches pages and revalidates them at intervals or on demand. The revalidateTag API provides granular cache invalidation by tag instead of whole-page revalidation, making it ideal for targeted updates.
Setting Up ISR with Tags for Granular Control
Start by configuring your page with getStaticProps and specifying a revalidate interval plus tags for cache invalidation. This ensures only relevant cached pages refresh when data changes.
Example: Order Dashboard Page
Here’s how to implement tag-based ISR for an order details page:
export async function getStaticProps({ params }) {
const order = await fetchOrder(params.id);
return {
props: { order },
revalidate: 60, // Revalidate every 60s if no trigger
tags: ['order-' + params.id]
};
}
Triggering Revalidation on Data Changes
Create an API route that updates your data source and calls revalidateTag when changes occur. This invalidates the specific cache tag, forcing Next.js to regenerate the page on the next request.
API Route for Order Updates
When an order status changes, trigger revalidation:
import { revalidateTag } from 'next/cache';
export async function POST(req) {
const { id, status } = await req.json();
await updateOrderStatus(id, status);
revalidateTag('order-' + id);
return Response.json({ success: true });
}
Client-Side Updates for Immediate Feedback
While server-side revalidation ensures fresh data on subsequent visits, clients need immediate updates. Combine ISR with client-side data fetching (e.g., React Query) to refresh UI without full page reloads.
React Query Integration Example
Use React Query’s useQuery with manual refetching:
const { data, refetch } = useQuery(['order', orderId], () => fetchOrder(orderId), {
initialData: initialOrderData
});
// After API update, refetch client-side
const handleUpdate = async () => {
await fetch('/api/update-order', { method: 'POST' });
await refetch(); // Refresh UI instantly
}
Practical Implementation: Order Status Dashboard
Here’s how these pieces work together for a live order tracking dashboard:
- Page loads static data via ISR with order-123 tag
- Admin updates order status via API route
- API calls revalidateTag(‘order-123’)
- Next.js queues page regeneration for next request
- Client-side React Query refetches data immediately after update
Performance Considerations and Best Practices
Optimize revalidation strategies to balance freshness and server load:
- Avoid over-revalidation: Use tags for specific data (e.g., order-123) instead of broad tags like orders
- Combine client and server: Use ISR for initial load + React Query for client-side refreshes
- Monitor revalidation frequency: Track cache hits/misses in Vercel analytics
- Set reasonable revalidate intervals: 60-300 seconds for frequently changing data
Conclusion
Combining Next.js ISR with revalidateTag delivers near real-time data updates while maintaining static site performance benefits. By implementing granular cache invalidation and client-side refetching strategies, you can build dynamic dashboards that feel instantaneous without compromising SEO or scalability. For your next project, prioritize tag-based revalidation for high-frequency data updates and pair it with React Query for seamless client-side experiences.