NextjsNext.js Cheatsheet

Next.js Cheatsheet

A dense, scannable reference for the file conventions, routing patterns, and code snippets used constantly in App Router development.

File conventions

File

Purpose

page.tsx

Makes a route segment publicly reachable and defines its UI

layout.tsx

Shared UI that wraps a segment and its children; preserved across navigations

loading.tsx

Instant loading UI shown via Suspense while a segment loads

error.tsx

Client Component error boundary UI for a segment

not-found.tsx

UI shown when notFound() is called or a route does not match

route.ts

A Route Handler — defines GET/POST/etc. functions for a real API endpoint

middleware.ts

Runs before requests complete routing, at the project root

template.tsx

Like layout.tsx, but re-mounts (fresh state) on every navigation

_menu.ts (project convention)

Sidebar/source-of-truth list of slugs and labels for a topic

Routing patterns

Folder name

Matches

about/

/about

blog/[slug]/

/blog/anythingparams.slug is "anything"

shop/[...slug]/

/shop/a/b/cparams.slug is ["a", "b", "c"]

shop/[[...slug]]/

/shop and /shop/a/b/c — optional catch-all

(marketing)/about/

/about — the group name is not part of the URL

@modal inside a layout

A named parallel slot, rendered alongside children

(.)photo/[id]/

Intercepts /photo/:id when navigated to from the same level

Data fetching

Fetch in a Server Component

TSX
export default async function Page() {
  const res = await fetch('https://api.example.com/items', {
    next: { revalidate: 60 }, // ISR: revalidate at most every 60s
  })
  const items = await res.json()
  return <ul>{items.map((i) => <li key={i.id}>{i.name}</li>)}</ul>
}

Static params for a dynamic route

TSX
export async function generateStaticParams() {
  const posts = await getPosts()
  return posts.map((post) => ({ slug: post.slug }))
}
Server Actions

A minimal form-bound Server Action

TSX
// app/actions.ts
'use server'
export async function createItem(formData: FormData) {
  const name = formData.get('name')
  // ...save to database...
}

// app/page.tsx
import { createItem } from './actions'

export default function Page() {
  return (
    <form action={createItem}>
      <input name="name" />
      <button type="submit">Add</button>
    </form>
  )
}
Metadata

Static and dynamic metadata

TSX
// Static
export const metadata = {
  title: 'My Page',
  description: 'A page description',
}

// Dynamic
export async function generateMetadata({ params }) {
  const { slug } = await params
  const post = await getPost(slug)
  return { title: post.title, description: post.excerpt }
}
next/image, next/font, next/link

Core built-in primitives

TSX
import Image from 'next/image'
import Link from 'next/link'
import { Inter } from 'next/font/google'

const inter = Inter({ subsets: ['latin'] })

export default function Page() {
  return (
    <main className={inter.className}>
      <Link href="/about">About</Link>
      <Image src="/hero.png" alt="Hero" width={800} height={400} priority />
    </main>
  )
}
Middleware

middleware.ts

TSX
import { NextResponse } from 'next/server'
import type { NextRequest } from 'next/server'

export function middleware(request: NextRequest) {
  const isLoggedIn = request.cookies.has('session')
  if (!isLoggedIn && request.nextUrl.pathname.startsWith('/dashboard')) {
    return NextResponse.redirect(new URL('/login', request.url))
  }
  return NextResponse.next()
}

export const config = {
  matcher: ['/dashboard/:path*'],
}