Skip to main content
All Docs
FeaturesBlockManOSUpdated March 26, 2026

How We Cut /pricing TTFB from 400 ms to <10 ms with ISR and Client Islands

How We Cut /pricing TTFB from 400 ms to <10 ms with ISR and Client Islands

Release: v1.0.81 · Date: 2026-03-26 · Ticket: PERF-19

The Problem

The BlockManOS /pricing page was opted into fully dynamic server-side rendering via export const dynamic = 'force-dynamic'. This meant every visitor — including unauthenticated users who had never touched the app — triggered a fresh Lambda invocation on each page load. The reason: the page called auth() on the server to decide whether to render a "Dashboard" or "Sign In" link in the navigation header.

The result was a Time to First Byte (TTFB) of 200–400 ms on cold starts, for a page whose content changes at most when pricing plans are updated — not on every request.

The /sign-in page had the same problem: no dynamic content whatsoever, yet it was also server-rendered on every request.

The Solution

1. ISR on /pricing

Incremental Static Regeneration (ISR) lets Next.js serve a pre-built, CDN-cached page and silently regenerate it in the background on a schedule. We set a one-hour revalidation window:

// src/app/pricing/page.tsx
export const revalidate = 3600;

This means the first request after a cache miss triggers a background rebuild. All subsequent requests within the hour hit the CDN with a TTFB of <10 ms.

2. The Client Island Pattern for Session-Aware UI

The only thing that was truly dynamic on the pricing page was a single nav link: "Dashboard" for signed-in users, "Sign In" for everyone else. Rather than blocking the entire page on a server-side session check, we extracted this into a minimal "use client" component:

// src/components/pricing-nav-link.tsx
"use client";

import Link from "next/link";
import { useSession } from "next-auth/react";

export function PricingNavLink() {
  const { data: session } = useSession();
  const userId = session?.user?.id ?? null;

  return userId ? (
    <Link href="/dashboard">Dashboard</Link>
  ) : (
    <Link href="/sign-in">Sign In</Link>
  );
}

The static page shell is delivered instantly from CDN cache. PricingNavLink hydrates on the client, fetches the session, and updates the link — all without blocking the initial render. From the user's perspective, the page loads near-instantly and the nav link resolves in a subsequent client-side tick.

This is the "client island" pattern: keep the page shell static, isolate dynamic behaviour into small client components that hydrate independently.

3. Full Static Generation on /sign-in

The sign-in page has no dynamic content at all — it renders a fixed layout with authentication provider buttons. We opted it into full static generation at build time:

// src/app/sign-in/[[...sign-in]]/page.tsx
export const dynamic = "force-static";

The page is now built once per deployment and served from the edge on every request.

4. /api/health Headers in vercel.json

As a related housekeeping change, we added explicit headers to the health-check endpoint to prevent accidental caching and to allow cross-origin requests:

// vercel.json
{
  "headers": [
    {
      "source": "/api/health",
      "headers": [
        { "key": "Cache-Control", "value": "no-store" },
        { "key": "Access-Control-Allow-Origin", "value": "*" }
      ]
    }
  ]
}

This ensures monitoring tools and external uptime checkers always receive a live response and are never served a stale cached result.

Performance Impact

PageBeforeAfterMechanism
/pricing200–400 ms TTFB (Lambda cold-start)<10 ms TTFB (CDN cache hit)ISR (revalidate = 3600)
/sign-inPer-request SSRBuild-time static, edge-servedforce-static

What Did Not Change

  • The pricing plan content and layout are identical.
  • The sign-in UI is identical.
  • Authenticated users still see "Dashboard" in the pricing nav — it just resolves client-side after hydration rather than server-side.
  • src/app/terms/page.tsx was intentionally left unchanged; its rendering strategy is controlled by the @saas-factory-live/shell package.
  • No API behaviour, billing logic, or data models were modified.