Add a spec-compliant /llms.txt to any Next.js app. Static file, App Router route handler, or build-time generation.
Next.js apps ship both static assets and server-rendered pages, so there are two equally valid homes for llms.txt. A static file in public/ is the simplest path; a dynamic route handler lets you pull from your CMS, file system, or docs collection at request time and set cache headers per deploy target.
App Router route handlers run on Node and the edge, so the same /llms.txt handler works on Vercel, Cloudflare, and AWS. For docs sites built with Contentlayer, Velite, or fumadocs, generating llms.txt at build time keeps the file in sync with your MDX collection with zero runtime cost.
Pick whichever fits your stack. For most teams the module or a static file is enough; reach for the dynamic route when you need request-time content.
Drop a file at public/llms.txt. No build step, no server logic.
```md # My Next.js Project > A short summary of what this project does and who it is for. ## Docs - [Getting Started](https://example.com/docs/getting-started): Quick setup guide - [API Reference](https://example.com/docs/api): Full API docs ## Examples - [Starter Template](https://example.com/examples/starter): Minimal Next.js starter ```
Generate at request time from a CMS, MDX collection, or database. Handler at app/llms.txt/route.ts.
```ts
import { NextResponse } from 'next/server'
export const revalidate = 3600
export async function GET() {
const body = await buildLlmsTxt()
return new NextResponse(body, {
headers: {
'Content-Type': 'text/plain; charset=utf-8',
'Cache-Control': 'public, max-age=0, s-maxage=3600, stale-while-revalidate=86400',
},
})
}
async function buildLlmsTxt() {
// Replace with your own content source (MDX, CMS, filesystem…)
return `# My Next.js Project\n\n> Short summary.\n\n## Docs\n\n- [Getting Started](https://example.com/docs/getting-started)\n`
}
```App Router route handler. Revalidates once per hour — swap `revalidate` for `dynamic = "force-dynamic"` if you want per-request generation.
Claude Code, Cursor, and Codex increasingly request markdown via the Accept: text/markdown header. Pairing your llms.txt with per-page markdown responses makes your whole site legible to agents, not just the index.
Generate llms.txt from your URL or browse the llms.txt directory for real examples.
For most sites, drop it in public/llms.txt — Next.js serves everything under public/ at the site root. Use an App Router route handler at app/llms.txt/route.ts when you need to generate the file dynamically from a CMS, MDX collection, or database.
Yes. Next.js serves static files from public/ before hitting the app router. If you keep both, delete public/llms.txt so your route handler receives requests.
Yes. Run a script in your prebuild step (for example, via a package.json "prebuild" hook) that writes public/llms.txt. This works with Contentlayer, Velite, fumadocs, or any MDX collection, and avoids runtime cost.
Yes. Route handlers marked with export const runtime = "edge" can return text/plain responses. For large llms-full.txt files, prefer the Node runtime so you have more memory headroom.
The spec accepts both. text/plain is the safer default for caching and CDN compatibility; text/markdown is the right content-type when you also want clients that negotiate on Accept: text/markdown to treat the response as markdown. You can send text/markdown; charset=utf-8 without breaking plain-text clients.