App Router, Server Components, SSR/SSG, API routes, and data fetching.
app/page.tsxapp/dashboard/page.tsx → /dashboard routelayout.tsxapp/dashboard/layout.tsx // wraps all /dashboard/* pagesloading.tsx / error.tsxapp/users/loading.tsx // shows skeleton while data loadsRoute Groups (folder)app/(marketing)/about/page.tsx → /about[slug] / [[...slug]]app/posts/[id]/page.tsx → /posts/123Parallel Routes @slotapp/@modal/(.)post/[id]/page.tsx // intercepting route modal'use client''use client'; export default function Counter() { const [c, setC] = useState(0); }Server Component (default)async function Page() { const data = await db.posts.findMany(); return <List data={data}/>; }Passing Server → Client<ClientButton label={serverData.title} /> // string OK, fn NOT OK'use server' (Server Actions)'use server'; export async function createPost(fd: FormData) { await db.insert(...) }Suspense + streaming<Suspense fallback={<Skeleton/>>}><SlowComponent/></Suspense>taint APIexperimental_taintObjectReference("No secrets client-side", secretObj)fetch(url, { cache })fetch('/api/posts', { next: { revalidate: 60 } }) // ISR every 60sgenerateStaticParams()export async function generateStaticParams() { return posts.map(p => ({slug: p.slug})) }revalidatePath / revalidateTagimport { revalidatePath } from 'next/cache'; revalidatePath('/posts')unstable_cache()const getPosts = unstable_cache(() => db.posts.findMany(), ['posts'], { tags: ['posts'], revalidate: 300 })Request Memoization// Both calls below make only ONE HTTP request per render: await getUser(); await getUser();cookies() / headers()import { cookies } from 'next/headers'; const token = cookies().get('token')app/api/route.tsexport async function GET(req: Request) { return Response.json({ ok: true }) }NextRequest / NextResponsereturn NextResponse.redirect(new URL('/login', req.url))middleware.ts (root)export const config = { matcher: ['/dashboard/:path*', '/api/:path*'] }Edge Runtimeexport const runtime = 'edge' // in route.ts or page.tsxnext/image <Image><Image src="/hero.jpg" width={1200} height={630} alt="Hero" priority />next/fontconst inter = Inter({ subsets: ['latin'], variable: '--font-inter' })generateMetadata()export async function generateMetadata({ params }) { return { title: (await getPost(params.slug)).title } }next/link prefetch<Link href="/about" prefetch={false}>About</Link>Bundle AnalyzerANALYZE=true npm run build // generates treemap HTML reportinstrumentation.tsexport async function register() { await setupTelemetry() }