// LOADING
// LOADING
// LOADING_ARTICLE
the fastest request is the one you never make. caching is how you avoid recomputing the same answer over and over — and done well, it is the single highest-leverage performance change you can ship. but "just add a cache" hides a stack of decisions. here is how the layers fit together.
think of caching as layers, each one closer to the user than the last:
every layer you can answer from is a database query and a render you did not have to do. the goal is to push as much as possible toward the top of the list.
the cheapest cache lives in the user's browser. correct Cache-Control headers, hashed asset filenames, and ETags mean repeat visits download almost nothing. i wrote a full breakdown in how browser caching works and why it matters for SEO — and yes, it affects ranking, because it improves core web vitals.
a cdn stores your responses in data centers around the world, so a user in singapore is served from singapore, not your origin in virginia. static assets are the obvious win, but you can also cache full html responses at the edge for pages that are the same for everyone. this pairs naturally with the serverless / edge model.
ISR is next.js's answer to "i want static speed but my content changes." you render a page once, serve the cached html to everyone, and revalidate it on an interval or on demand:
tsx// revalidate this page at most once an hour export const revalidate = 3600; // ...or invalidate instantly when content changes import { revalidatePath } from "next/cache"; revalidatePath("/notelogs");
choosing between static, ISR, and fully dynamic is exactly the trade-off in SSR vs SSG in Next.js. most content sites should default to static or ISR and only go dynamic where they truly need per-request data.
when a page is dynamic — personalized dashboards, expensive aggregations — you still do not want to hit the database on every request. redis sits between your app and the database as a fast, shared, in-memory store:
tsasync function getDashboard(userId: string) { const key = `dashboard:${userId}`; const cached = await redis.get(key); if (cached) return JSON.parse(cached); const data = await buildExpensiveDashboard(userId); await redis.set(key, JSON.stringify(data), "EX", 300); // 5-min TTL return data; }
design your data access so caching is easy to bolt on — clean, predictable endpoints help, which is the theme of how to design APIs for Next.js applications.
there are only two hard things in computer science: cache invalidation and naming things.
the failure mode of every cache is serving stale data. two reliable strategies:
revalidatePath after a publish, or deleting a redis key on update).most production systems use both: a short TTL as a safety net, plus targeted invalidation on writes for freshness.
a typical fast next.js stack layers all four:
Cache-Controlcaching is one pillar of a fast site; rendering strategy and shipped-javascript are the others. see SSR vs SSG, React Server Components, and Core Web Vitals in 2026 for the rest of the picture. building something that needs to scale? let's talk.