Loading...
Loading...
Evaluates Next.js routes and outputs optimal revalidate settings, cache tags for ISR, SSR configurations, or streaming patterns. This skill should be used when optimizing Next.js caching strategies, configuring Incremental Static Regeneration, planning cache invalidation, or choosing between SSR/ISR/SSG. Use for Next.js caching, revalidation, ISR, cache tags, on-demand revalidation, or rendering strategies.
npx skill4agent add hopeoverture/worldbuilding-app-skills revalidation-strategy-planner// app/about/page.tsx
export default async function AboutPage() {
// Generated at build time, no revalidation
return <div>About Us</div>;
}// 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} />;
}// app/dashboard/page.tsx
export const dynamic = 'force-dynamic';
export default async function Dashboard() {
const data = await fetchUserData();
return <DashboardView data={data} />;
}// app/timeline/page.tsx
import { Suspense } from 'react';
export default function TimelinePage() {
return (
<div>
<TimelineHeader />
<Suspense fallback={<TimelineLoader />}>
<TimelineEvents />
</Suspense>
</div>
);
}references/rendering-strategies.md// Revalidate every 60 seconds
export const revalidate = 60;
// Revalidate every hour
export const revalidate = 3600;
// Revalidate every day
export const revalidate = 86400;// 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 server';
import { revalidatePath } from 'next/cache';
export async function updateEntity(id: string, data: EntityData) {
await saveEntity(id, data);
revalidatePath(`/entities/${id}`);
revalidatePath('/entities');
}// 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} />;
}import { revalidateTag } from 'next/cache';
// Revalidate all pages with 'entities' tag
revalidateTag('entities');
// Revalidate specific entity
revalidateTag(`entity-${entityId}`);assets/cache-tag-patterns.tsscripts/analyze_routes.pypython scripts/analyze_routes.py ./appreferences/decision-matrix.md// 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} />;
}// 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} />;
}// 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} />;
}// 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>
);
}// After entity update
revalidateTag(`entity-${entityId}`);
// After relationship change
revalidateTag(`entity-${sourceId}`);
revalidateTag(`entity-${targetId}`);
revalidateTag('relationships');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}`);
}
}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);
}
}export const revalidate = 60; // Revalidate every minute
export const dynamic = 'force-static'; // Serve stale while revalidatingexport 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} />;
}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>
);
}// middleware.ts
export function middleware(request: NextRequest) {
const start = Date.now();
return NextResponse.next({
headers: {
'x-response-time': `${Date.now() - start}ms`,
},
});
}