JOURNAL / PERSPECTIVES

Next.js rendering and cache trade-offs

Edge rendering can be a performance win—or an expensive distraction. Here’s a no-nonsense decision framework for choosing SSR/ISR/SSG (and partial prerendering-style patterns), designing caches that survive real traffic, and measuring what actually moves Core Web Vitals.

DECEMBER 8, 2025 · 11 MIN READ · BLANCHE
Next.js rendering and cache trade-offs
FIG. 01 — FROM THE BLANCHE JOURNALBLANCHE / 2026

INTRODUCTION

Edge rendering is having a moment—but most teams adopt it the way they adopt a new JavaScript library: because it sounds faster.

The uncomfortable truth: edge rendering is a latency trade. Sometimes it reduces time-to-first-byte (TTFB). Sometimes it increases it. And it almost always increases system complexity.

This article gives you a pragmatic framework to choose the right rendering mode in Next.js (SSR/ISR/SSG and partial-prerendering-style patterns), design caching that holds up under real traffic, and instrument performance so you can prove wins (and catch regressions) before users do.


What “performance” means in 2026 (CWV + UX)

If you’re still optimizing for “fast page load” as a single number, you’re optimizing for a world that no longer exists.

In practice, modern web performance is:

  • Core Web Vitals (CWV): especially LCP (Largest Contentful Paint), INP (Interaction to Next Paint), and CLS (Cumulative Layout Shift)
  • User-perceived responsiveness: does the UI feel immediate, stable, and predictable?
  • Consistency: p95 and p99 matter more than your best-case lab run
  • Operational performance: deploy safety, cache correctness, incident frequency, and cost

The performance trap: optimizing TTFB while ignoring INP

Edge rendering often improves TTFB, which can help LCP on content-heavy pages. But many real-world regressions come from:

  • shipping too much JavaScript (hurts INP)
  • client-side data waterfalls (hurts LCP and perceived speed)
  • layout instability from late-loading content (hurts CLS)

Callout: If your LCP element is an image or hero text that can be served statically, edge rendering is rarely your biggest lever. Your biggest lever is usually cacheability + payload discipline.

Concrete takeaway: treat edge rendering as one tool in a broader system—rendering mode + caching + payload + observability.


Rendering modes in Next.js: a quick, honest comparison

Next.js gives you multiple ways to produce HTML. The right choice depends on data volatility, personalization, and cache strategy.

SSG (Static Site Generation)

Best for: marketing pages, docs, pricing, content that changes infrequently.

  • Pros: fastest and cheapest at scale; naturally CDN-cacheable; stable CWV
  • Cons: rebuilds for content changes unless paired with ISR; personalization requires client-side or edge middleware tricks

Use when: the page can be correct even if it’s minutes or hours stale.

ISR (Incremental Static Regeneration)

Best for: content sites, product catalogs, landing pages with periodic updates.

  • Pros: keeps SSG benefits while allowing updates; great with stale-while-revalidate behavior
  • Cons: cache invalidation and revalidation logic can become subtle; “freshness” is probabilistic under load

Use when: you want mostly-static pages with controlled freshness.

SSR (Server-Side Rendering)

Best for: authenticated dashboards, highly dynamic pages, personalized experiences.

  • Pros: HTML reflects the latest data and user context; simpler correctness model than complicated cache hacks
  • Cons: can be expensive; harder to cache; p95/p99 can suffer under load; can hide client waterfalls rather than fixing them

Use when: correctness matters more than cacheability.

Edge rendering (SSR at the edge)

Best for: global audiences with cache misses, request-time routing, lightweight personalization, A/B buckets.

  • Pros: can reduce latency by executing closer to the user; good for fast request-time decisions
  • Cons: adds constraints (runtime limits, libraries, cold starts depending on platform); can increase latency if it forces extra network hops to your data; debugging and observability can be harder

Use when: you can keep edge logic thin and your data access is edge-friendly.

Partial prerendering-style patterns (hybrid HTML)

Even if you’re not using a specific branded feature, the pattern is clear: serve a mostly-static shell fast and stream or hydrate the dynamic parts.

  • Static frame: navigation, layout, headings, critical content

  • Dynamic islands: user-specific panels, recommendations, cart state

  • Pros: improves LCP while keeping personalization; reduces server work for unchanged sections

  • Cons: can introduce complexity in data boundaries; requires discipline to avoid turning “islands” into waterfalls

Use when: you need both fast first paint and dynamic sections.

Rule of thumb: If the page can be cached, prefer a cached strategy (SSG/ISR + CDN). If it can’t, prefer SSR—but fight for a hybrid approach that keeps the LCP-critical content cacheable.


A decision matrix by page type (marketing, dashboard, docs, ecom)

Instead of choosing a rendering mode per app, choose it per page type.

The matrix: what to optimize for

Evaluate each page on:

  1. Personalization level: none / light (locale, experiment) / heavy (user-specific data)
  2. Freshness requirement: hours / minutes / seconds
  3. Traffic shape: spiky campaigns vs. steady usage
  4. Global distribution: local vs. worldwide
  5. Data proximity: can your data be accessed quickly from the edge?

Marketing pages

Default: SSG or ISR

  • Cache at the CDN with long TTLs
  • Use ISR revalidation for CMS-driven updates
  • Keep JS small; don’t ship your entire app shell

When edge helps:

  • request-time geo/locale routing
  • experiment assignment (but avoid per-user HTML variation)

Concrete example: a campaign landing page built with SSG + CDN caching will often beat edge SSR simply because it avoids runtime work entirely.

Docs and content hubs

Default: SSG + ISR

  • Pre-render pages, cache aggressively
  • Use tag-based invalidation tied to content IDs or sections
  • Prefer static search indexes or a dedicated search provider (Algolia/Meilisearch) rather than server-rendering search results

When edge helps:

  • region-based mirrors
  • auth gating for private docs (but keep the docs HTML cacheable when possible)

Authenticated dashboards

Default: SSR (often at the origin) + selective caching of data

Dashboards are usually:

  • heavily personalized
  • data changes frequently
  • sensitive to correctness

A good pattern:

  • SSR the shell and critical above-the-fold data
  • cache shared reference data (feature flags, plan limits, static metadata)
  • stream or lazy-load secondary panels

When edge helps:

  • lightweight auth/session checks
  • routing to nearest region

When edge hurts:

  • if every request triggers multiple calls back to a centralized database region

Litmus test: If edge SSR still needs to round-trip to us-east-1 for database queries, you may be adding an extra hop and increasing tail latency.

E-commerce (category, PDP, cart, checkout)

E-commerce needs a split brain:

  • Category pages (PLP): ISR + CDN caching; tolerate slight staleness
  • Product detail pages (PDP): ISR for core content + dynamic pricing/availability as an island
  • Cart/checkout: SSR, minimal JS, ruthless performance budgets

Caching pitfalls here:

  • personalization (recommendations) can destroy cache hit rate
  • price/availability can be region-specific and time-sensitive

A pragmatic approach:

  • cache the PDP HTML with a short TTL or ISR
  • fetch price/stock via a fast API with aggressive caching at the data layer

Real-world reference: teams often combine CDN-cached product pages with edge logic for geo/currency and origin SSR only for checkout.


Caching strategies that survive real traffic

Caching is where performance is won or lost. The goal isn’t “cache everything.” The goal is:

  • maximize cache hit rate
  • keep correctness
  • keep invalidation understandable

1) CDN caching: the default performance multiplier

If your HTML can be cached, do it.

Practical guidelines:

  • Use long TTL for truly static assets
  • For HTML, use stale-while-revalidate behavior where appropriate
  • Vary carefully: every Vary dimension can explode your cache

Key takeaway: cache keys are product decisions. If you vary by user, you’ve opted out of CDN scale.

2) Stale-while-revalidate (SWR): speed with controlled freshness

SWR works because it:

  • serves a cached response immediately (fast LCP)
  • refreshes in the background (eventual freshness)

Use SWR for:

  • marketing pages tied to a CMS
  • product catalogs
  • docs that update periodically

Avoid SWR for:

  • security-sensitive or transactional flows
  • pages where “stale” is incorrect (balances, inventory in checkout)

3) Tag-based invalidation: the only sane way to scale ISR

Time-based revalidation is simple but blunt. Tag-based invalidation lets you invalidate precisely.

Pattern:

  • Tag pages and data by entity: product:123, category:boots, doc:getting-started
  • When content changes, invalidate by tag

This keeps:

  • rebuild/revalidation targeted
  • freshness high without killing cache hit rate

4) Personalization pitfalls: death by cache fragmentation

Personalization is the fastest way to turn a 90% cache hit rate into 0%.

Common mistakes:

  • rendering user-specific content in the main HTML
  • varying HTML by too many dimensions (user, plan, locale, experiment, device)
  • embedding session-derived content in server components that forces dynamic rendering everywhere

Better patterns:

  • keep the page shell cacheable
  • load personalized sections as client-side islands or streamed fragments
  • use edge middleware for coarse decisions (locale, country, experiment bucket) but keep the number of variants small

Callout: If you can’t explain your cache key in one sentence, you’re building an incident.


Common anti-patterns: what actually makes Next.js apps slow

1) Data waterfalls (server or client)

A waterfall happens when request B waits on request A unnecessarily.

Fixes:

  • fetch in parallel where possible
  • collapse multiple backend calls into a purpose-built BFF endpoint
  • push aggregation down to the database when it’s safe

Tooling references:

  • use Server-Timing to expose backend phases
  • use tracing (OpenTelemetry) to see dependency chains

2) Oversized JavaScript and “app shell everywhere”

You can edge render the world and still fail CWV if you ship a heavy client bundle.

Fixes:

  • enforce route-level bundle budgets
  • prefer server components where they reduce client JS (without forcing dynamic rendering unnecessarily)
  • audit third-party scripts ruthlessly (tag managers, chat widgets, A/B tooling)

3) “Dynamic by default” pages

A surprisingly common failure mode in modern frameworks is making pages dynamic unintentionally:

  • reading cookies in a way that forces dynamic rendering
  • mixing personalized data into shared pages
  • disabling caching globally “to be safe”

Fix:

  • decide per route: static, revalidated, or dynamic
  • isolate personalization to specific components
  • document the caching contract for each page type

Observability: measuring wins and regressions

If you can’t measure it, you can’t ship it safely.

What to instrument

  1. RUM (Real User Monitoring)

    • Track CWV by route template, device class, and geography
    • Tools: Google web-vitals + your analytics pipeline, or vendors like SpeedCurve, Datadog RUM, New Relic
  2. Synthetic monitoring

    • Catch regressions before users do
    • Tools: Lighthouse CI, WebPageTest, Playwright-based performance tests
  3. Server timing and tracing

    • Add Server-Timing headers for:
      • cache status (HIT/MISS)
      • render time
      • upstream API time
      • database time
    • Use distributed tracing (OpenTelemetry) to see end-to-end latency

How to interpret results (the part teams get wrong)

  • Look at p75 for CWV compliance, but also track p95 for UX consistency
  • Segment by cache hit vs miss: many “fast” sites are only fast on hits
  • Compare before/after by route template, not by whole-site averages

Concrete takeaway: a change that improves median LCP but worsens p95 TTFB may be a net negative for real users.


Reference architecture: a pragmatic Next.js performance stack

A pattern that works across most teams:

1) Route classification

  • Static (SSG): marketing, docs index, evergreen pages
  • Revalidated (ISR + tags): content pages, product pages, category pages
  • Dynamic (SSR): account, cart, checkout, admin

2) Cache layering

  • CDN cache for HTML where possible
  • Data cache for shared fetches (reference data, catalogs)
  • Client cache for user-specific data (React Query/SWR) with careful hydration

3) Edge usage: thin and intentional

Use the edge for:

  • routing (geo/locale)
  • bot detection and basic request shaping
  • lightweight experiment assignment

Avoid the edge for:

  • heavy rendering that depends on centralized databases
  • complex dependency graphs that are hard to debug under edge constraints

Rollout checklist: ship performance improvements without drama

  1. Pick one page type (e.g., PDP or marketing landing pages)
  2. Define the goal metric (e.g., p75 LCP down by 300ms; maintain INP)
  3. Map the cache key and document it
  4. Implement rendering mode + caching deliberately (don’t “dynamic by default”)
  5. Add instrumentation:
    • RUM route segmentation
    • Server-Timing for cache/render/upstream
    • synthetic tests for key flows
  6. Run an A/B or gradual rollout
  7. Watch p95 and error rates (not just averages)
  8. Lock in budgets:
    • JS bundle limits per route
    • third-party script approvals
    • performance gates in CI (Lighthouse CI thresholds)

Conclusion: the fastest Next.js app is the one that can be cached

Edge rendering is powerful—but it’s not a shortcut to great performance. The teams that consistently hit strong Core Web Vitals do three things well:

  • choose rendering modes per page type, not per ideology
  • design caches with explicit keys and invalidation, not hope
  • measure outcomes with RUM + server timing + synthetic, not vibes

If you want a practical next step: audit your top 10 routes by traffic and revenue, classify them (static/revalidated/dynamic), then redesign caching and personalization boundaries to maximize cache hits without breaking correctness. That’s where the biggest, most repeatable wins live.

KEEP THINKING

Keep reading.

More ideas on building better brands and digital products.

DESIGN / ACCESSIBILITY

Accessibility is a design advantage.

ENGINEERING / PERSPECTIVE

Beyond parallax.

View all articles