Skip to content

Next.js — Overview

OWNER: floris, floor
ALSO_USED_BY: urszula, maxim, alexander
LAST_VERIFIED: 2026-03-26
GE_STACK_VERSION: next@15.5.x (pinned), react@19.x, react-dom@19.x


Overview

Next.js App Router is GE's primary web framework for all client SaaS projects.
All new projects use App Router exclusively. Pages Router is legacy — never start new work with it.
GE pins to Next.js 15.5.x stable. Next.js 16 upgrade is tracked by Joshua (Innovation).


GE Version Pinning

CHECK: package.json has exact minor version pinned
IF: version is ^15 or ~15 (floating)
THEN: pin to 15.5.x — GE does not float majors or minors

{
  "next": "15.5.3",
  "react": "19.1.0",
  "react-dom": "19.1.0",
  "typescript": "5.7.x"
}

CHECK: next.config.ts uses TypeScript config (not .js or .mjs)
CHECK: turbopack is enabled for dev (next dev --turbopack)
CHECK: output: "standalone" is set for Docker/k3s deployment


When to Use What

Rendering Strategy Decision

IF: page is public, content rarely changes (marketing, docs)
THEN: Static Generation (SSG) with ISR revalidation

IF: page is authenticated, shows user-specific data
THEN: Server-Side Rendering (SSR) with no-cache

IF: page has mix of static shell + dynamic personalized content
THEN: Partial Prerendering (PPR) — static shell + streamed dynamic holes

IF: page is purely interactive (dashboard widgets, real-time)
THEN: Server Component wrapper + Client Component leaves

Feature Selection

IF: form submission or data mutation
THEN: Server Actions (not API routes)
READ_ALSO: wiki/docs/stack/nextjs/data-fetching.md

IF: auth check before page load
THEN: Middleware with JWT verification
READ_ALSO: wiki/docs/stack/nextjs/middleware.md

IF: SEO-critical page
THEN: generateMetadata() in layout/page, not

IF: complex multi-panel UI (modal over page, sidebar + main)
THEN: Parallel routes + intercepting routes
READ_ALSO: wiki/docs/stack/nextjs/app-router.md


GE Project Structure Convention

src/
├── app/                    # Routes only — no business logic here
│   ├── (public)/           # Route group: unauthenticated pages
│   ├── (dashboard)/        # Route group: authenticated pages
│   ├── api/                # API routes (minimal — prefer Server Actions)
│   ├── layout.tsx          # Root layout (required)
│   ├── not-found.tsx       # Global 404
│   └── error.tsx           # Global error boundary
├── components/
│   ├── ui/                 # shadcn/ui primitives (auto-generated)
│   └── {feature}/          # Feature-specific components
├── lib/
│   ├── actions/            # Server Actions
│   ├── db/                 # Drizzle schema + queries
│   ├── services/           # Business logic (pure functions)
│   └── utils/              # Shared utilities
├── hooks/                  # Client-side React hooks
├── types/                  # TypeScript type definitions
└── styles/                 # Global CSS (Tailwind entry)

ANTI_PATTERN: putting business logic in app/ route files
FIX: extract to lib/services/ — route files import and call

ANTI_PATTERN: creating components/ inside app/ route folders
FIX: use src/components/{feature}/ — colocated components cause import confusion


GE-Specific Conventions

  1. TypeScript strict mode always ("strict": true in tsconfig)
  2. All styling via Tailwind CSS + shadcn/ui — no CSS modules, no styled-components
  3. Drizzle ORM for database access — never raw SQL in components
  4. Server Actions for mutations — API routes only for webhooks/external integrations
  5. output: "standalone" in next.config for Docker builds
  6. Environment variables: NEXT_PUBLIC_ prefix only for client-safe values
  7. All data EU-hosted — no Vercel Analytics, no US-based CDN origins
  8. Images served via next/image with explicit width/height — no unoptimized
  9. Error boundaries at route segment level (error.tsx), not global try-catch
  10. Loading states via loading.tsx + Suspense boundaries, not client spinners

next.config.ts Baseline

import type { NextConfig } from "next";

const nextConfig: NextConfig = {
  output: "standalone",
  typescript: {
    ignoreBuildErrors: false, // GE: NEVER set to true
  },
  eslint: {
    ignoreDuringBuilds: false, // GE: NEVER set to true
  },
  images: {
    remotePatterns: [
      // Add per-project CDN domains here
    ],
    formats: ["image/avif", "image/webp"],
  },
  experimental: {
    ppr: "incremental", // Partial Prerendering per-route opt-in
    typedRoutes: true,
  },
  headers: async () => [
    {
      source: "/(.*)",
      headers: [
        { key: "X-Frame-Options", value: "DENY" },
        { key: "X-Content-Type-Options", value: "nosniff" },
        { key: "Referrer-Policy", value: "strict-origin-when-cross-origin" },
        { key: "Permissions-Policy", value: "camera=(), microphone=(), geolocation=()" },
      ],
    },
  ],
};

export default nextConfig;

CHECK: ignoreBuildErrors is false
CHECK: ignoreDuringBuilds is false
CHECK: output is "standalone"
CHECK: security headers are present


Upgrade Path: Next.js 15 → 16

TOOL: pnpm dlx @next/codemod@canary upgrade latest

KEY_CHANGES:
- Turbopack becomes default bundler (already used in GE dev)
- middleware.ts renamed to proxy.ts (runs Node.js, not Edge)
- PPR stabilized via cacheComponents config (replaces experimental.ppr)
- React Compiler support (stable)
- useFormState replaced by useActionState

IF: upgrading to Next.js 16
THEN: coordinate with Joshua (Innovation) + full team discussion
THEN: update GE_STACK_VERSION in ALL wiki pages under wiki/docs/stack/nextjs/


Security

CHECK: patched against CVE-2025-29927 (middleware bypass via x-middleware-subrequest)
IF: self-hosted (GE always is — k3s, not Vercel)
THEN: must be on Next.js 15.2.3+ (GE pins 15.5.x — safe)

CHECK: no secrets in NEXT_PUBLIC_ env vars
CHECK: Server Actions validate input with Zod (.issues not .errors — Zod v4)
CHECK: CSRF protection on Server Actions (Next.js built-in, verify not disabled)


Cross-References

READ_ALSO: wiki/docs/stack/nextjs/app-router.md
READ_ALSO: wiki/docs/stack/nextjs/server-components.md
READ_ALSO: wiki/docs/stack/nextjs/data-fetching.md
READ_ALSO: wiki/docs/stack/nextjs/middleware.md
READ_ALSO: wiki/docs/stack/nextjs/performance.md
READ_ALSO: wiki/docs/stack/nextjs/pitfalls.md
READ_ALSO: wiki/docs/stack/nextjs/checklist.md