# Next.js Patterns Quick Reference > App Router (Next.js 13.4+). Pages Router patterns not covered. ## App Router File Conventions | File | Purpose | Renders | |------|---------|---------| | `page.tsx` | Route UI (makes route publicly accessible) | Server Component | | `layout.tsx` | Shared UI wrapping children (preserved on nav) | Server Component | | `template.tsx` | Like layout but re-mounts on navigation | Server Component | | `loading.tsx` | Instant loading UI (Suspense boundary) | Server Component | | `error.tsx` | Error boundary for segment | **Client Component** | | `not-found.tsx` | UI for `notFound()` calls | Server Component | | `route.ts` | API endpoint (GET, POST, etc.) | N/A | | `default.tsx` | Fallback for parallel routes | Server Component | | `middleware.ts` | Runs before requests (root only) | Edge Runtime | | `opengraph-image.tsx` | Dynamic OG image generation | Edge Runtime | ### Route Segment Folders | Pattern | Example | Purpose | |---------|---------|---------| | Static | `app/about/page.tsx` | `/about` | | Dynamic | `app/blog/[slug]/page.tsx` | `/blog/hello-world` | | Catch-all | `app/docs/[...slug]/page.tsx` | `/docs/a/b/c` | | Optional catch-all | `app/docs/[[...slug]]/page.tsx` | `/docs` or `/docs/a/b` | | Route group | `app/(marketing)/about/page.tsx` | Groups without URL segment | | Parallel route | `app/@modal/login/page.tsx` | Simultaneous route slots | | Intercepted route | `app/(.)photo/[id]/page.tsx` | Intercept navigation | --- ## Caching Layers Summary | Layer | What | Where | Duration | Opt-out | |-------|------|-------|----------|---------| | Request Memoization | `fetch()` dedup in single render | Server | Per request | `AbortController` | | Data Cache | `fetch()` results | Server | Persistent | `cache: 'no-store'` or `revalidate: 0` | | Full Route Cache | Static HTML + RSC payload | Server | Persistent | Dynamic functions or `revalidate` | | Router Cache | RSC payload | Client | Session (30s dynamic, 5min static) | `router.refresh()` | ### Revalidation Strategies ```typescript // Time-based fetch(url, { next: { revalidate: 60 } }); // Revalidate every 60s // On-demand (from API route or Server Action) import { revalidatePath, revalidateTag } from "next/cache"; revalidatePath("/blog"); // Revalidate path revalidateTag("posts"); // Revalidate by tag // Tag a fetch for on-demand revalidation fetch(url, { next: { tags: ["posts"] } }); // Opt out entirely fetch(url, { cache: "no-store" }); // Route segment config export const dynamic = "force-dynamic"; // Always dynamic export const revalidate = 60; // Segment-level ISR ``` --- ## Data Fetching Patterns ### Server Component (default, preferred) ```typescript // Async Server Component - fetch directly async function BlogPage() { const posts = await fetch("https://api.example.com/posts", { next: { revalidate: 3600 } }).then(r => r.json()); return ; } ``` ### Parallel Data Fetching ```typescript async function Dashboard() { // Start all fetches simultaneously const [user, orders, stats] = await Promise.all([ getUser(), getOrders(), getStats(), ]); return ; } ``` ### Streaming with Suspense ```typescript export default function Page() { return (

Dashboard

{/* Shows instantly */} {/* Streams in when ready */} }> }>
); } ``` ### Cached Server Functions ```typescript import { cache } from "react"; // Deduplicated across components in same request export const getUser = cache(async (id: string) => { const res = await fetch(`/api/users/${id}`); return res.json(); }); ``` --- ## Server Action Patterns ### Basic Form Action ```typescript // app/actions.ts "use server"; import { revalidatePath } from "next/cache"; import { redirect } from "next/navigation"; export async function createPost(formData: FormData) { const title = formData.get("title") as string; const body = formData.get("body") as string; await db.post.create({ data: { title, body } }); revalidatePath("/posts"); redirect("/posts"); } ``` ```typescript // In a Server Component import { createPost } from "./actions"; export default function NewPost() { return (