revalidation-strategy-planner
Revalidation Strategy Planner
Analyze Next.js application routes and recommend optimal caching and revalidation strategies for performance and data freshness.
Overview
To optimize Next.js caching strategies:
- Analyze route characteristics (data freshness requirements, update frequency)
- Determine appropriate rendering strategy (SSG, ISR, SSR, streaming)
- Configure revalidation intervals for ISR routes
- Implement cache tags for on-demand revalidation
- Set up streaming for progressive page loading
Rendering Strategies
Static Site Generation (SSG)
To use SSG for rarely changing content:
// app/about/page.tsx
export default async function AboutPage() {
// Generated at build time, no revalidation
return <div>About Us</div>;
}
Best for:
- Marketing pages
- Documentation
- Static content that rarely changes
Incremental Static Regeneration (ISR)
To use ISR for periodically updated content:
// app/entities/[id]/page.tsx
export const revalidate = 3600; // Revalidate every hour
export default async function EntityPage({ params }: { params: { id: string } }) {
const entity = await fetchEntity(params.id);
return <EntityDetail entity={entity} />;
}
Best for:
- Entity detail pages
- Blog posts
- Product listings
- Content with predictable update patterns
Server-Side Rendering (SSR)
To use SSR for real-time data:
// app/dashboard/page.tsx
export const dynamic = 'force-dynamic';
export default async function Dashboard() {
const data = await fetchUserData();
return <DashboardView data={data} />;
}
Best for:
- User dashboards
- Personalized content
- Real-time data displays
- Authentication-dependent pages
Streaming
To use streaming for progressive loading:
// app/timeline/page.tsx
import { Suspense } from 'react';
export default function TimelinePage() {
return (
<div>
<TimelineHeader />
<Suspense fallback={<TimelineLoader />}>
<TimelineEvents />
</Suspense>
</div>
);
}
Best for:
- Pages with slow data fetching
- Complex pages with multiple data sources
- Improving perceived performance
Consult references/rendering-strategies.md for detailed strategy comparison.
Revalidation Configuration
Time-Based Revalidation
To set revalidation intervals:
// Revalidate every 60 seconds
export const revalidate = 60;
// Revalidate every hour
export const revalidate = 3600;
// Revalidate every day
export const revalidate = 86400;
On-Demand Revalidation
To implement on-demand cache invalidation:
// app/api/revalidate/route.ts
import { revalidatePath, revalidateTag } from 'next/cache';
import { NextRequest } from 'next/server';
export async function POST(request: NextRequest) {
const { path, tag } = await request.json();
if (path) {
revalidatePath(path);
}
if (tag) {
revalidateTag(tag);
}
return Response.json({ revalidated: true, now: Date.now() });
}
Use from Server Actions:
'use server';
import { revalidatePath } from 'next/cache';
export async function updateEntity(id: string, data: EntityData) {
await saveEntity(id, data);
revalidatePath(`/entities/${id}`);
revalidatePath('/entities');
}
Cache Tags
To implement cache tag-based revalidation:
// app/entities/[id]/page.tsx
export default async function EntityPage({ params }: { params: { id: string } }) {
const entity = await fetch(`/api/entities/${params.id}`, {
next: {
tags: [`entity-${params.id}`, 'entities'],
},
});
return <EntityDetail entity={entity} />;
}
Revalidate by tag:
import { revalidateTag } from 'next/cache';
// Revalidate all pages with 'entities' tag
revalidateTag('entities');
// Revalidate specific entity
revalidateTag(`entity-${entityId}`);
Reference assets/cache-tag-patterns.ts for cache tagging patterns.
Route Analysis
Use scripts/analyze_routes.py to analyze application routes and recommend strategies:
python scripts/analyze_routes.py ./app
Output includes:
- Route path
- Recommended rendering strategy
- Suggested revalidation interval
- Appropriate cache tags
- Reasoning for recommendations
Analysis Criteria
Consider these factors:
-
Data Freshness Requirements
- Real-time: SSR or very short revalidation (1-60s)
- Near real-time: ISR with short interval (60-300s)
- Periodic updates: ISR with medium interval (300-3600s)
- Rarely changes: SSG or long interval (3600s+)
-
Update Frequency
- Continuous: SSR
- Multiple times per hour: ISR (60-300s)
- Hourly: ISR (3600s)
- Daily: ISR (86400s)
- Weekly+: SSG
-
Personalization
- User-specific: SSR
- Role-based: SSR or ISR with user context
- Public: SSG or ISR
-
Data Source Performance
- Fast (<100ms): Any strategy
- Medium (100-500ms): Consider streaming
- Slow (>500ms): Use streaming or aggressive caching
Consult references/decision-matrix.md for the complete decision matrix.
Implementation Patterns
Entity Detail Pages
To optimize entity pages:
// app/entities/[id]/page.tsx
export const revalidate = 1800; // 30 minutes
export async function generateStaticParams() {
const entities = await fetchAllEntityIds();
return entities.map((id) => ({ id: id.toString() }));
}
export default async function EntityPage({ params }: { params: { id: string } }) {
const entity = await fetchEntity(params.id, {
next: { tags: [`entity-${params.id}`, 'entities'] },
});
return <EntityDetail entity={entity} />;
}
List Pages
To optimize listing pages:
// app/entities/page.tsx
export const revalidate = 300; // 5 minutes
export default async function EntitiesPage({
searchParams,
}: {
searchParams: { page?: string };
}) {
const page = parseInt(searchParams.page || '1');
const entities = await fetchEntities(page, {
next: { tags: ['entities'] },
});
return <EntityList entities={entities} />;
}
Timeline Pages
To optimize timeline with streaming:
// app/timeline/page.tsx
import { Suspense } from 'react';
export default function TimelinePage() {
return (
<div>
<Suspense fallback={<TimelineHeaderSkeleton />}>
<TimelineHeader />
</Suspense>
<Suspense fallback={<EventsSkeleton />}>
<TimelineEvents />
</Suspense>
</div>
);
}
async function TimelineEvents() {
const events = await fetchTimelineEvents({
next: { tags: ['timeline'], revalidate: 600 },
});
return <EventsList events={events} />;
}
Dashboard Pages
To implement personalized dashboard:
// app/dashboard/page.tsx
export const dynamic = 'force-dynamic';
export default async function DashboardPage() {
const session = await getSession();
const data = await fetchUserDashboard(session.userId);
return (
<div>
<Suspense fallback={<StatsSkeleton />}>
<DashboardStats userId={session.userId} />
</Suspense>
<Suspense fallback={<ActivitySkeleton />}>
<RecentActivity userId={session.userId} />
</Suspense>
</div>
);
}
Cache Invalidation Strategies
Granular Invalidation
To invalidate specific resources:
// After entity update
revalidateTag(`entity-${entityId}`);
// After relationship change
revalidateTag(`entity-${sourceId}`);
revalidateTag(`entity-${targetId}`);
revalidateTag('relationships');
Cascade Invalidation
To invalidate related resources:
async function updateEntity(id: string, data: EntityData) {
await saveEntity(id, data);
// Invalidate entity page
revalidateTag(`entity-${id}`);
// Invalidate list pages
revalidateTag('entities');
// Invalidate related pages
const relationships = await getEntityRelationships(id);
for (const rel of relationships) {
revalidateTag(`entity-${rel.targetId}`);
}
}
Batch Invalidation
To invalidate multiple resources efficiently:
async function bulkUpdateEntities(updates: EntityUpdate[]) {
await saveBulkUpdates(updates);
// Collect unique tags
const tags = new Set<string>(['entities']);
for (const update of updates) {
tags.add(`entity-${update.id}`);
}
// Revalidate all at once
for (const tag of tags) {
revalidateTag(tag);
}
}
Performance Optimization
Stale-While-Revalidate
To implement SWR pattern:
export const revalidate = 60; // Revalidate every minute
export const dynamic = 'force-static'; // Serve stale while revalidating
Parallel Data Fetching
To fetch data in parallel:
export default async function EntityPage({ params }: { params: { id: string } }) {
const [entity, relationships, timeline] = await Promise.all([
fetchEntity(params.id),
fetchRelationships(params.id),
fetchTimeline(params.id),
]);
return <EntityDetailView entity={entity} relationships={relationships} timeline={timeline} />;
}
Selective Streaming
To stream only slow components:
export default function EntityPage({ params }: { params: { id: string } }) {
return (
<div>
<EntityHeader id={params.id} /> {/* Fast, no streaming */}
<Suspense fallback={<RelationshipsSkeleton />}>
<EntityRelationships id={params.id} /> {/* Slow, stream it */}
</Suspense>
</div>
);
}
Monitoring and Testing
To monitor cache performance:
- Cache Hit Rates: Track ISR cache hits vs. regenerations
- Revalidation Frequency: Monitor how often pages regenerate
- Response Times: Measure time to first byte (TTFB)
- Stale Serving: Track stale-while-revalidate occurrences
Use Next.js analytics or custom logging:
// middleware.ts
export function middleware(request: NextRequest) {
const start = Date.now();
return NextResponse.next({
headers: {
'x-response-time': `${Date.now() - start}ms`,
},
});
}
Best Practices
- Start Conservative: Begin with shorter revalidation intervals, increase gradually
- Use Cache Tags: Prefer tag-based invalidation over path-based
- Monitor Performance: Track cache hit rates and response times
- Plan Invalidation: Design invalidation strategy with data mutations
- Test Edge Cases: Verify behavior with stale data and revalidation
- Document Decisions: Record why specific intervals were chosen
- Consider Users: Balance freshness with performance
Troubleshooting
Common issues:
- Stale Data Persisting: Check cache tag implementation and invalidation logic
- Excessive Regeneration: Increase revalidation interval or fix trigger-happy invalidation
- Slow Page Loads: Add streaming for slow components
- Cache Not Working: Verify fetch options and dynamic/static configuration
- Development vs Production: Remember ISR only works in production builds
More from hopeoverture/worldbuilding-app-skills
eslint-prettier-husky-config
This skill should be used when setting up code quality tooling with ESLint v9 flat config, Prettier formatting, Husky git hooks, lint-staged pre-commit checks, and GitHub Actions CI lint workflow. Apply when initializing linting, adding code formatting, configuring pre-commit hooks, setting up quality gates, or establishing lint CI checks for Next.js or React projects.
51testing-next-stack
Scaffolds comprehensive testing setup for Next.js applications including Vitest unit tests, React Testing Library component tests, and Playwright E2E flows with accessibility testing via axe-core. This skill should be used when setting up test infrastructure, generating test files, creating test utilities, adding accessibility checks, or configuring testing frameworks for Next.js projects. Trigger terms include setup testing, scaffold tests, vitest, RTL, playwright, e2e tests, component tests, unit tests, accessibility testing, a11y tests, axe-core, test configuration.
38markdown-editor-integrator
This skill should be used when installing and configuring markdown editor functionality using @uiw/react-md-editor. Applies when adding rich text editing, markdown support, WYSIWYG editors, content editing with preview, or text formatting features. Trigger terms include markdown editor, rich text editor, text editor, add markdown, install markdown editor, markdown component, WYSIWYG, content editor, text formatting, editor preview.
27form-generator-rhf-zod
This skill should be used when generating React forms with React Hook Form, Zod validation, and shadcn/ui components. Applies when creating entity forms, character editors, location forms, data entry forms, or any form requiring client and server validation. Trigger terms include create form, generate form, build form, React Hook Form, RHF, Zod validation, form component, entity form, character form, data entry, form schema.
23supabase-auth-ssr-setup
This skill should be used when configuring Supabase Auth for server-side rendering with Next.js App Router, including secure cookie handling, middleware protection, route guards, authentication utilities, and logout flow. Apply when setting up SSR auth, adding protected routes, implementing middleware authentication, configuring secure sessions, or building login/logout flows with Supabase.
18tailwind-shadcn-ui-setup
This skill should be used when setting up, configuring, or initializing Tailwind CSS (v3 or v4) and shadcn/ui for Next.js 16 App Router projects. Configure dark mode, design tokens, base layout with header/sidebar, accessibility defaults, and generate example components. Includes comprehensive setup automation, theme customization, and production-ready patterns. Use when the user requests "setup Tailwind", "configure shadcn/ui", "add dark mode", "initialize design system", or "setup UI framework" for Next.js projects.
17