Data Fetching

Documentation content can be sourced from various locations — local files, a CMS, a Git repository, or an API. Next.js 16 provides flexible patterns for fetching and caching this content.

Server Components

Server Components are the default in the App Router, making them ideal for fetching documentation content at build or request time without shipping additional JavaScript to the client.

app/docs/[slug]/page.tsx
import { getDocBySlug } from "@/lib/docs"

interface Props {
  params: Promise<{ slug: string }>
}

export default async function DocPage({ params }: Props) {
  const { slug } = await params
  const doc = await getDocBySlug(slug)

  return (
    <article className="docs-prose">
      <h1>{doc.title}</h1>
      <div dangerouslySetInnerHTML={{ __html: doc.content }} />
    </article>
  )
}

Fetching content

For markdown-based documentation, fetch and parse content on the server:

lib/docs.ts
import fs from "node:fs/promises"
import path from "node:path"
import matter from "gray-matter"

const DOCS_DIR = path.join(process.cwd(), "content/docs")

export async function getDocBySlug(slug: string) {
  const filePath = path.join(DOCS_DIR, `${slug}.mdx`)
  const source = await fs.readFile(filePath, "utf-8")
  const { data, content } = matter(source)

  return {
    title: data.title as string,
    description: data.description as string,
    content,
  }
}

Static content

For fully static documentation, use the "use cache" directive to pre-render pages at build time:

app/docs/[slug]/page.tsx
"use cache"

export default async function DocPage({ params }: Props) {
  const { slug } = await params
  const doc = await getDocBySlug(slug)
  return <article>{/* ... */}</article>
}

Cache Components

Next.js 16 introduces Cache Components with the "use cache" directive. This allows fine-grained control over which parts of your page are cached.

Client-side data

For interactive features like search or live previews, use SWR to fetch data on the client:

components/search.tsx
"use client"

import useSWR from "swr"

const fetcher = (url: string) => fetch(url).then(r => r.json())

export function SearchResults({ query }: { query: string }) {
  const { data, isLoading } = useSWR(
    query ? `/api/search?q=${query}` : null,
    fetcher
  )

  if (isLoading) return <p>Searching...</p>
  return <ul>{data?.results.map(/* ... */)}</ul>
}