# Updating large-scale site navigation with minimal revalidation

**Author:** Dom Sipowicz, Ismael Rumzan

---

## Introduction

When building large-scale websites with thousands or millions of pages using Incremental Static Regeneration (ISR), updating navigation data can cause revalidating every page that includes the navigation component. Since each page regeneration consumes server resources, this widespread revalidation will result in large resource usage in terms of function duration, bandwidth, and hits on the backend.

With Vercel, you can render navigation using build-time data for fast loads, then update it using a shared endpoint to keep all pages current without individual revalidation.

## Overview

In this guide, you'll learn:

- How to apply smart revalidation to update navigation without triggering full ISR revalidation for every page, thereby reducing function duration, bandwidth, and backend load.
  
- How to fetch navigation data on the client once per session and reuse it across pages for a consistent, fresh experience.
  
- How lightweight server-side caching ensures fast responses without overwhelming infrastructure.
  

## How it works

Your navigation data stays fresh whilst maintaining good UX using the following process:

1. The initial render at build time uses server data for instant paint and no layout shift.
   
2. The application fetches a shared navigation endpoint once and reuses the data across pages in the same session
   
3. Server-side caching refreshes the endpoint periodically to keep responses current without forcing ISR revalidation of every page
   
4. Manual triggers such as a CMS webhook or an client-side button, can revalidate the navigation instantly
   
5. SWR handles recovery, automatically fetching fresh navigation data when a user reconnects to the internet
   

﻿This ensures users always see up-to-date navigation while browsing the site, whether through client-side transitions or full page reloads.

Let's review how to implement this by exploring an existing demo repository.

### 1\. Set up the demo repository

Start by cloning and running the [MegaNav Demo repository](https://github.com/vercel-solutions/meganav-demo) to see the implementation in action:

`git clone https://github.com/vercel-solutions/meganav-demo.git cd meganav-demo pnpm install pnpm run dev`

1. Open [http://localhost:3000](http://localhost:3000/) to see the demo running.
   
2. Click on **Products** in the top navigation
   
3. Notice how the navigation shows timestamps that update in real-time without affecting page performance
   

### 2\. Client-side: Fresh data every 10 seconds via SWR polling

SWR provides automatic polling to keep navigation data fresh on the client side. The following component is imported in the `app/layout.tsx` file and used in all pages:

`const { data: nav } = useSWR<NavigationData>('/api/navigation', fetcher, { fallbackData, revalidateOnFocus: false, revalidateOnMount: true, revalidateOnReconnect: true, refreshInterval: 10000, // 10 seconds for demonstration purposes only }); // Ensure we always have data to render const navigationData = nav || fallbackData;`

> Setting `refreshInterval` to 10 seconds is for demonstration purposes only. In a production environment, you will not need `refreshInterval` as `revalidateOnMount` and `revalidateOnReconnect` are sufficient.

#### Test SWR behavior:

1. Open the browser's Network tab
   
2. Watch for requests to `/api/navigation` every 10 seconds
   
3. Notice how the navigation timestamps updates automatically
   

### 3\. Server-side: Cache refreshed every 5 minutes

The navigation API uses server-side caching with a 5-minute revalidation period. This ensures fresh data without overwhelming your server.

`import { getNavigationData } from '@/lib/mock-nav'; import { NextResponse } from 'next/server'; export const revalidate = 360; // every 5 minutes for demonstration purposes export async function GET() { // needs to be wrapped in the async function in order bundler not to pack it as static const data = await getNavigationData(); return NextResponse.json(data); }`

> In a production environment, you can set the navigation route revalidate time to 1 day and update it with [on-demand ISR](https://vercel.com/docs/incremental-static-regeneration/quickstart#on-demand-revalidation).

### 4\. Manual: Instant refresh with revalidation button

You can trigger immediate navigation updates using ISR on-demand revalidation.

`import { revalidatePath } from 'next/cache'; import { NextResponse } from 'next/server'; export async function GET() { try { revalidatePath('/api/navigation'); return NextResponse.json({ revalidated: true, now: Date.now() }); } catch (err) { console.log(err); return NextResponse.json( { message: 'Error revalidating' }, { status: 500 }, ); }}`

#### Test manual revalidation:

1. Open [http://localhost:3000](http://localhost:3000/)
   
2. Click on **Products** in the top navigation and notice the timestamp for each item
   
3. Click the **Simulate Headless CMS Change** button
   
4. Watch the navigation timestamps update immediately
   
5. Note that the page content timestamp does not change
   

## Implementing this with a CMS

You can implement this system directly with a headless CMS. Instead of using a button, you will trigger revalidation with specific CMS events such as content publishing. You can do this with many CMSs using a webhook.

## Best Practices

### Caching Strategy

- **Navigation API**: Use on-demand ISR with 1 day time-based revalidation. If you use time-based revalidation only, set it to 5-15 minutes for fresh data.
  
- **ISR Pages**: 1-24 hours revalidation for stable content
  

## Next Steps

- [**MegaNav Demo Repository**](https://github.com/vercel-solutions/meganav-demo) - Clone and experiment with the complete implementation
  
- [**Next.js App Router Documentation**](https://nextjs.org/docs/app/building-your-application/routing) - Learn more about Next.js routing and API routes
  
- [**SWR Documentation**](https://swr.vercel.app/) - Explore advanced data fetching patterns

---

[View full KB sitemap](/kb/sitemap.md)
