Next.js Cheatsheet

App Router and Routing

Use this Next.js reference while you build software engineering projects, review code for technical interview prep, or polish examples for a software engineer resume.

File-System Routing Basics

Every folder in app/ that contains a page.tsx becomes a route segment.

app/
├── page.tsx           →  /
├── about/
│   └── page.tsx       →  /about
├── blog/
│   ├── page.tsx       →  /blog
│   └── [slug]/
│       └── page.tsx   →  /blog/:slug
└── dashboard/
    ├── layout.tsx     →  shared layout for /dashboard/*
    ├── page.tsx       →  /dashboard
    └── settings/
        └── page.tsx   →  /dashboard/settings

Special Files in a Segment

FileDescription
page.tsxUI for the route; makes it publicly accessible
layout.tsxShared wrapper; persists across navigations
template.tsxLike layout but re-mounts on every navigation
loading.tsxSuspense fallback shown while page/data loads
error.tsxError boundary (must be 'use client')
not-found.tsxRendered when notFound() is called
route.tsAPI endpoint (no page.tsx in same folder)
default.tsxFallback for parallel routes

Dynamic Segments

app/blog/[slug]/page.tsx          →  /blog/anything
app/shop/[...segments]/page.tsx   →  /shop/a/b/c  (catch-all)
app/shop/[[...segments]]/page.tsx →  /shop  AND  /shop/a/b/c  (optional catch-all)
// app/blog/[slug]/page.tsx
export default async function BlogPost({
  params,
}: {
  params: Promise<{ slug: string }>
}) {
  const { slug } = await params
  return <h1>{slug}</h1>
}

Next 15+: params and searchParams are Promises — always await them.

Route Groups

Wrap a folder in (parens) to group routes without affecting the URL.

app/
├── (marketing)/
│   ├── layout.tsx       ← layout only for marketing pages
│   ├── page.tsx         →  /
│   └── about/
│       └── page.tsx     →  /about
└── (app)/
    ├── layout.tsx       ← layout only for app pages
    └── dashboard/
        └── page.tsx     →  /dashboard

Parallel Routes

Render multiple pages simultaneously in the same layout using named slots (@folder).

app/
└── dashboard/
    ├── layout.tsx
    ├── page.tsx
    ├── @analytics/
    │   └── page.tsx
    └── @team/
        └── page.tsx
// app/dashboard/layout.tsx
export default function DashboardLayout({
  children,
  analytics,
  team,
}: {
  children: React.ReactNode
  analytics: React.ReactNode
  team: React.ReactNode
}) {
  return (
    <div>
      {children}
      {analytics}
      {team}
    </div>
  )
}

Intercepting Routes

Show a route inside the current layout without full navigation (e.g., modal).

app/
├── feed/
│   └── page.tsx             →  /feed
├── photo/
│   └── [id]/
│       └── page.tsx         →  /photo/123  (full page)
└── feed/
    └── (.)photo/[id]/       →  intercepts /photo/123 when navigating from /feed
        └── page.tsx

Convention: (.) same level · (..) one level up · (..)(..) two levels · (...) root.

useRouter (Client Component)

'use client'
import { useRouter } from 'next/navigation'

export function Nav() {
  const router = useRouter()

  return (
    <button onClick={() => router.push('/dashboard')}>Go</button>
  )
}
MethodEffect
router.push(href)Navigate; adds history entry
router.replace(href)Navigate; replaces history entry
router.back()Go back
router.forward()Go forward
router.refresh()Re-fetch current route server data
router.prefetch(href)Manually prefetch

Import from 'next/navigation' (App Router), NOT 'next/router' (Pages Router).

usePathname, useSearchParams, useParams

'use client'
import { usePathname, useSearchParams, useParams } from 'next/navigation'

function Nav() {
  const pathname = usePathname()       // '/dashboard/settings'
  const searchParams = useSearchParams()
  const q = searchParams.get('q')      // ?q=value
  const params = useParams()           // { slug: 'my-post' }
}

Wrap components using useSearchParams in <Suspense> to avoid build errors.

redirect and notFound (Server-side)

import { redirect, notFound } from 'next/navigation'

export default async function Page() {
  const user = await getUser()

  if (!user) redirect('/login')         // throws internally — no return needed
  if (!user.active) notFound()          // renders not-found.tsx

  return <div>Welcome {user.name}</div>
}

permanentRedirect

import { permanentRedirect } from 'next/navigation'

permanentRedirect('/new-path')   // 308 Permanent Redirect

Linking to External URLs

// Use a regular <a> for external links
<a href="https://example.com" target="_blank" rel="noopener noreferrer">
  External
</a>