The caching layers that make a storefront feel instant
Sub-second browsing comes from caching the right things in the right places: the CDN, the server and the browser, each with a clear job.
2 min read
- Performance
- Next.js
- Redis
"Make it faster" usually turns into a pile of unrelated tweaks. For an e-commerce catalogue, the biggest wins come from thinking in layers: what can be served from the edge, what can be computed once on the server, and what the browser can remember.
Layer 1: the CDN serves the page
Product listing and detail pages are read far more than they change. Render them statically and revalidate on a schedule or on demand when a product is edited:
// app/products/[slug]/page.tsx
export const revalidate = 300; // re-render at most every 5 minutes
export default async function ProductPage({ params }) {
const { slug } = await params;
const product = await getProduct(slug);
return <Product product={product} />;
}When an admin updates a product, call revalidatePath for that page so the change appears immediately instead of waiting for the timer.
Layer 2: the server caches expensive reads
Some data is shared by many pages but costly to compute: category trees, facet counts, "related products". Cache those in Redis with a short TTL and a key that includes everything that affects the result.
async function getFacets(category: string) {
const key = `facets:v2:${category}`;
const hit = await redis.get(key);
if (hit) return JSON.parse(hit);
const facets = await computeFacets(category);
await redis.set(key, JSON.stringify(facets), { EX: 120 });
return facets;
}Version the key (v2) so a change to the shape of the data never serves stale objects in the old format.
Layer 3: the database does less work
Before adding more cache, check the queries. An index that matches the filter and sort order of the listing page often turns a slow query into a fast one:
db.products.createIndex({ category: 1, inStock: 1, price: 1 });Paginate with a cursor (the last seen sort value) rather than large offsets, which get slower the deeper a shopper browses.
Layer 4: the browser remembers
- Serve images in modern formats at the size they are displayed, with explicit dimensions to avoid layout shift.
- Prefetch the next likely page when a product card enters the viewport.
- Keep the cart in client state and sync it in the background, so adding an item never waits on the network.
Measure what shoppers feel
Track Largest Contentful Paint and Interaction to Next Paint from real users, split by page type. Lab scores are useful for catching regressions, but field data tells you whether browsing actually feels instant on the phones your customers use.
Each layer has one job. When a page feels slow, you know exactly which layer to look at.
Found this useful? Share it with someone building the same thing.