Skip to main content
All Docs
FeaturesBlockManOSUpdated March 26, 2026

Performance: Smarter Static Generation in v1.0.86

Performance: Smarter Static Generation in v1.0.86

PERF-22 · v1.0.86 · PR #62

Overview

v1.0.86 ships targeted rendering-strategy fixes that reduce unnecessary serverless function invocations on the dashboard navigation shell and the /sign-up route. No end-user behaviour changes.

Background: Next.js rendering modes

Next.js App Router supports three rendering modes for route segments:

DirectiveBehaviour
(default)Static or ISR depending on data-fetching
force-dynamicAlways server-rendered on every request (SSR)
force-staticAlways pre-rendered at build time (SSG)

Using force-dynamic on a segment that has no dynamic data is wasteful — it spins up a serverless function for each request when a cached or pre-built response would do.

What changed

1. Dashboard layout shell — force-dynamic removed

The dashboard layout shell (src/app/dashboard/layout.tsx) previously carried a blanket force-dynamic export. This caused the layout segment to be server-rendered on every navigation, even though:

  • The shell contains only Link elements and "use client" components.
  • Session data (useSession, OrgSwitcher, UserMenu) is fetched client-side.
  • No auth() call is made server-side inside the layout itself.

Removing force-dynamic from the layout allows Next.js to treat the navigation shell as a cacheable segment. Each dashboard child page that genuinely requires server-side auth (auth() call) already exports its own force-dynamic — those pages are unaffected.

// Before — src/app/dashboard/layout.tsx
export const dynamic = 'force-dynamic'; // ← removed

import Link from "next/link";
// ...
// After — src/app/dashboard/layout.tsx
import Link from "next/link";
// No segment-level dynamic export; child pages control their own rendering.

2. Sign-up page — marked force-static

The /sign-up route is a compile-time redirect to /sign-in. It has no dynamic data, no session access, and no database queries. Without an explicit directive it could be subject to unnecessary dynamic evaluation.

Adding force-static ensures it is pre-rendered at build time:

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

import { redirect } from "next/navigation";

/** Sign-up is handled by OAuth providers — redirect to sign-in. */
export default function SignUpPage() {
  redirect("/sign-in");
}

3. Middleware — /api/health moved to matcher exclusion

Previously the middleware function contained an explicit if-block to bypass auth checks for /api/health and /api/health/*. These entries have been removed in favour of excluding the path at the matcher level:

// src/middleware.ts — config.matcher
'/((?!_next/static|_next/image|favicon\.ico|tailwindcss-browser\.js|api/health).*)'

Excluding the path from the matcher entirely means the middleware function is never invoked for health-check requests, which is marginally more efficient and keeps the allow-list simpler.

Rendering strategy reference

For completeness, the full rendering strategy across key routes after this release:

RouteStrategyDirective
/ISR (60 s)revalidate = 60
/pricingISR (1 h)revalidate = 3600
/sign-inStaticforce-static
/sign-upStaticforce-static ✨ new
/dashboard (shell)Cacheable shell(default, removed force-dynamic) ✨ new
Dashboard child pagesDynamic (SSR)force-dynamic (per-page)

Impact

  • Fewer cold-starts on the dashboard navigation shell and /sign-up.
  • Simplified middleware allow-list — one fewer branching condition per request.
  • No behaviour change — auth-protected dashboard pages remain fully dynamic.