Cheat SheetsNext.jsFoundations

Foundations — Cheat Sheet

Next.js · 7 topics. Download the PDF or the Instagram carousel and share it.

Cheat Sheet · AiCanCode.org
Foundations
Next.js7 topicsQuick revision reference
1

Rendering Models — CSR, SSR, SSG, ISR and RSC

Five ways to turn a component into HTML, differing in where and when it runs. Choosing per route rather than per application is the thing Next.js actually gives you.

  • Next.js lets each route choose its rendering model instead of committing the whole app to one
  • Static plus ISR gives CDN speed with controlled staleness; dynamic rendering costs a server round trip per visit
  • Reading cookies, headers or searchParams opts a route into dynamic rendering automatically
  • Server components run only on the server: they can await data directly and their code never reaches the browser
  • Server components hold data and structure; anything with state, effects or event handlers must be a client component
Where and when the HTML is produced
// CSR — Client-Side Rendering
//   The server sends an empty div. React builds the page in the browser.
//   + cheap to host, great for logged-in app screens
//   - blank until JS loads, weak for SEO, slow on a mid-range phone

// SSG — Static Site Generation (build time)
//   HTML produced once, at deploy, served from a CDN.
//   + fastest possible, cheapest, perfect SEO
//   - stale until the next deploy; not for per-user content
//   Next: the default for a route with no dynamic data.

// ISR — Incremental Static Regeneration
//   Static, but regenerated in the background every N seconds.
//   + CDN speed with fresh-enough content
//   - a user can see a stale copy for up to N seconds
export const revalidate = 3600

// SSR — Server-Side Rendering (per request)
//   HTML built on the server for each request.
//   + always fresh, personalised, SEO-friendly
//   - a server round trip on every visit; you pay for compute

// RSC — React Server Components (App Router)
//   Components that run ONLY on the server and stream their output.
//   Their code — and the libraries they import — never reach the browser.
//   This is the default in the App Router, not an opt-in.
2

The App Router — Files Are the Routing

A folder is a URL segment and a handful of reserved filenames give you the page, its layout, its loading state and its error boundary. There is no route configuration to write.

  • Folders are URL segments and reserved filenames declare what each segment renders — there is no route config
  • Only page.tsx or route.ts makes a segment routable, so components can safely be colocated inside route folders
  • Reading searchParams forces a route to be dynamic; useSearchParams in a small client component keeps the rest static
  • generateStaticParams pre-renders known dynamic paths at build time
  • Route groups organise without changing the URL, and _folders are never routable
The reserved filenames
app/
  layout.tsx            // root layout — REQUIRED, wraps everything
  page.tsx              // /
  loading.tsx           // Suspense fallback for this segment
  error.tsx             // error boundary ('use client', required)
  not-found.tsx         // 404 for this segment
  global-error.tsx      // catches errors in the root layout itself

  problems/
    page.tsx            // /problems
    loading.tsx         // shown while /problems streams
    [slug]/
      page.tsx          // /problems/two-sum
      opengraph-image.tsx

  api/
    health/route.ts     // GET /api/health — a handler, not a page

// Only page.tsx and route.ts make a segment publicly routable. A
// folder of components with no page.tsx is not a URL — which is why
// colocating components inside a route folder is safe.

// Dynamic segments
[slug]        // /problems/two-sum   -> params.slug
[...path]     // catch-all           -> params.path is an array
[[...path]]   // optional catch-all  -> also matches the parent
3

Server and Client Components

Everything is a server component until a file says otherwise. Knowing exactly what "use client" does — and what it does not — is the single most important thing in the App Router.

  • "use client" marks a bundle boundary, not a runtime — client components still render on the server for the initial HTML
  • The directive is inherited through imports, so one at the top of a tree pulls the whole tree into the bundle
  • Push "use client" down to the leaves, and pass server-rendered content through as children
  • Props crossing into a client component must be serializable — functions and class instances cannot cross
  • Those props are embedded in the HTML, so select fields explicitly and use the server-only package to guard server modules
It marks a bundle boundary, not a runtime
'use client'      // at the TOP of a file, before imports

// It means: this module and every module it imports goes into the
// client bundle. It does NOT mean "does not run on the server" —
// a client component still renders on the server for the initial
// HTML, then hydrates in the browser.

// The boundary is inherited DOWNWARD through imports:
// ClientThing.tsx has 'use client'
//   -> everything it imports is client too, even without the directive
// So one 'use client' at the top of a tree pulls the whole tree in.

// Server components cannot:
//   useState  useEffect  useRef  useContext
//   onClick and any event handler
//   window  document  localStorage
//   any browser-only library

// Client components cannot:
//   be async / await data directly
//   import server-only code (a database client, a secret)
//   read the filesystem

// The error that teaches this:
//   "You're importing a component that needs useState. It only works
//    in a Client Component, but none of its parents are marked with
//    'use client'."
4

Navigation — Link, useRouter and the Router Cache

Link prefetches on hover and swaps only the part of the tree that changed. The surprises come from the client-side cache sitting between you and fresh data.

  • Link prefetches in the viewport and swaps only the changed segment, keeping layouts mounted
  • Import useRouter from next/navigation — next/router is the Pages Router and will not work
  • The client router cache is the usual cause of stale data after a save; router.refresh or revalidatePath clears it
  • Next 15 sets the dynamic router cache to 0 by default, which removed most staleness complaints
  • Update filters with router.replace and scroll: false so the page neither stacks history nor jumps to the top
next/navigation, and prefetch on view
import Link from 'next/link'

<Link href="/problems">Problems</Link>
<Link href={`/problems/${slug}`} prefetch={false}>Open</Link>
<Link href="/dashboard" replace>Go</Link>          // no history entry

// Link prefetches when it enters the viewport (production only), so
// the destination is often already loaded before the click. That is
// most of why App Router navigation feels instant.

// Programmatic — note the import path
'use client'
import { useRouter } from 'next/navigation'        // NOT next/router
const router = useRouter()
router.push('/problems/two-sum')
router.replace('/login')
router.back()
router.refresh()          // re-fetch the current route from the server

// Reading the current location, all from next/navigation:
const pathname = usePathname()                     // '/problems/two-sum'
const params = useParams()                         // { slug: 'two-sum' }
const search = useSearchParams()                   // read-only

// useSearchParams makes a client component dynamic, so wrap it in a
// Suspense boundary or the whole route opts out of static rendering.
5

Layouts, Loading States and Error Boundaries

A layout persists across navigation while its children change. loading.tsx and error.tsx are Suspense and error boundaries you get by naming a file.

  • Layouts persist across navigation between their children, so their client state and scroll position survive
  • Because a layout does not re-run on those navigations, per-page data belongs in the page, not the layout
  • loading.tsx is a Suspense fallback for the segment; finer Suspense boundaries inside a page stream each slow part separately
  • error.tsx must be a client component, contains failures to its segment, and receives a reset function
  • notFound() and redirect() work by throwing, so calling them inside a try/catch swallows them
Layouts persist; templates remount
// app/layout.tsx — the root. Required, and must render html and body.
export default function RootLayout({ children }) {
  return (
    <html lang="en">
      <body>
        <Header />
        {children}
        <Footer />
      </body>
    </html>
  )
}

// app/(app)/dashboard/layout.tsx — nests inside the root
export default function DashboardLayout({ children }) {
  return <div className="grid grid-cols-[240px_1fr]"><Sidebar />{children}</div>
}

// Navigating /dashboard -> /dashboard/billing re-renders only the
// page. The sidebar is NOT re-rendered, so its scroll position, its
// open sections and any client state inside it survive.

// The corollary that surprises people: a layout does not re-run on
// navigation between its children, so data fetched in a layout is
// NOT refreshed by moving between them. Fetch per-page data in the
// page.

// template.tsx is the escape hatch — same shape, but a fresh instance
// on every navigation. Use it when you want an enter animation or
// state reset per route.
6

Project Structure and TypeScript Conventions

The app directory is routing, not architecture. Deciding what belongs beside a route and what belongs in a shared layer is what keeps a Next codebase navigable past twenty routes.

  • The app directory describes URLs; domain logic and shared components belong outside it
  • Underscore-prefixed folders are never routable, which makes colocating route-specific code safe
  • server-only and client-only turn a wrong import into a build failure instead of a leak
  • typedRoutes makes a mistyped href a compile error, and Next 15 params must be awaited
  • Settle route naming, a per-feature queries file and a per-feature actions file before the codebase grows
app/ is URLs; everything else lives outside
src/
  app/
    layout.tsx
    page.tsx
    problems/
      page.tsx
      loading.tsx
      _components/ProblemCard.tsx     // used by this route only
      _lib/format.ts
    (auth)/
      login/page.tsx
      register/page.tsx
    api/
      webhooks/razorpay/route.ts
  components/
    ui/Button.tsx                     // design-system primitives
    shared/EmptyState.tsx
  lib/
    api-client.ts                     // transport
    db.ts                             // server-only
    auth.ts
  features/
    problems/                         // domain logic used by several routes
      queries.ts
      actions.ts
      types.ts

// The rule: app/ describes URLs. Anything that is not about a URL —
// a domain model, a query, a shared component — belongs outside it.

// _folders are never routable, which is what makes colocation safe.
// Prefix server-only modules and enforce it:
import 'server-only'          // in lib/db.ts — a client import fails the build
import 'client-only'          // in a browser-API module
7

Hydration Errors and the Usual Traps

The server rendered one thing and the browser expected another. Hydration mismatches have a short list of causes, and knowing it turns a baffling error into a two-minute fix.

  • A hydration error means the server HTML and the client render disagree for the same props
  • The causes are dates, randomness, browser APIs read during render, invalid HTML nesting, and browser extensions
  • Prefer rendering after mount; suppressHydrationWarning covers one element and ssr: false is a last resort
  • Format dates and numbers with an explicit locale or the build machine and the user will disagree
  • Dev renders per request and skips caching — always verify with a production build before trusting a fix
Time, randomness, browser APIs, bad nesting
// 1. A value that differs between server and client
new Date().toLocaleString()      // server timezone vs the user's
Math.random()
Date.now()
// Fix: render it in an effect, or pass a fixed value down.

// 2. A browser API read during render
if (window.innerWidth < 768)     // ReferenceError on the server
localStorage.getItem('theme')    // undefined on the server
// Fix: read it in useEffect, and accept one frame of the default.

// 3. Invalid HTML nesting — the browser repairs it, React does not
<p><div>…</div></p>              // the browser closes the p early
<a><a>…</a></a>
<table><div/></table>
// This one is easy to miss, because the error points at hydration
// rather than at the markup.

// 4. Extensions that mutate the DOM before hydration
//    Password managers and translators add attributes to inputs.
//    Test in an incognito window before chasing your own code.

// 5. Locale or currency formatting without a fixed locale
new Intl.NumberFormat().format(n)          // varies by machine
new Intl.NumberFormat('en-IN').format(n)   // deterministic
Learn this free with Aria, your AI tutor → AiCanCode.org/learn/nextjs