Nextjs llms.txt: Generate llms-full.txt and .md Pages

You want coding agents and AI assistants to read your docs the way you wrote them, without the nav, cookie banner and script tags around every page. A nextjs llms.txt setup does that in three pieces: an llms.txt index at the root, an llms-full.txt with everything in one file, and a clean Markdown twin of every page at its URL plus .md. This tutorial builds all three with App Router route handlers, generated from the same data your pages render, and shows what we learned shipping them for the Wingo UI component catalog.
Every snippet is TypeScript for Next.js 16 and React 19. More on how agents read component libraries is in Building UI with AI agents.
What is llms.txt and what goes in it?
llms.txt is a Markdown file at your site root that tells a language model what the site is and where the useful pages are. Jeremy Howard published the llms.txt proposal (opens in a new tab) on September 3, 2024, and last revised it on August 10, 2026 (checked October 16, 2026).
The format is short:
- An H1 with the site name. It is the only required part.
- A blockquote with a summary.
- Optional paragraphs or lists with details (no headings).
- Any number of
##sections, each a list of links in the form[name](url): notes. - A section named
## Optionalholds secondary links that an agent can skip when it needs a shorter context.
The proposal also asks for a clean Markdown version of each page at the same URL with .md added (page.html.md), or with .md in place of the extension (page.md). Its current version adds a rel="alternate" link of type text/markdown that points to that version, either as an HTML <link> element or as an HTTP Link header.
llms-full.txt comes from community practice, and the proposal does not mention it: the full text of the pages in one file. Next.js publishes all three for its own docs, at nextjs.org/docs/llms.txt (opens in a new tab), nextjs.org/docs/llms-full.txt (opens in a new tab) and a .md version of every docs page (checked October 16, 2026).
What does a real llms.txt example look like?
Here is the top of ours on October 16, 2026, trimmed. The full file is at /llms.txt:
Two choices in it are worth copying. The counts and the release number are computed from the registry at build time, so the summary is never out of date. And each line carries both URLs: the HTML page for people and the .md twin for agents. The OTP Input docs and /components/otp-input.md are the same docs in two formats.
Rendered for people, the same Markdown is a readable index. The demo below runs a slice of our file through the Markdown component.
$ npx wingo-ui@latest add markdownWhere should the content come from?
From the same source your pages render. If llms.txt is a hand-written file in public/, it is wrong the first time someone adds a page and forgets it. Our catalog has one meta.ts per component, and the HTML docs page, the .md twin, llms.txt, llms-full.txt and the MCP server all read those objects.
For this tutorial, the source is a typed module. Swap the array for your CMS client, your MDX loader or your registry; the rest of the code only needs docs and getDoc.
How do I generate llms.txt in the App Router?
Put the formatting in one module, then serve it from a route handler. The module builds the page Markdown once and reuses it for the twin and the full file.
The route handler lives in a folder named after the file. Route handlers are not cached by default, so force-static tells Next.js to run it once at build time and serve the result as a static file.
$ npx wingo-ui@latest add code-blockThe handler in the demo, as text:
If you turned on cacheComponents, delete the dynamic and dynamicParams exports in every file of this tutorial: Next.js 16 removed them under Cache Components. A GET handler that reads no request data then prerenders on its own, uncached data goes in a helper marked use cache, and the if (!doc) checks below still return the 404s.
How do I add llms-full.txt in Next.js?
With a second route handler that calls the other function. The whole Next.js llms-full.txt route:
Watch the size. On October 16, 2026 our dev server served a 5.3 MB /llms-full.txt (the install guide, the design rules, every item's props and usage, and the blog posts) and a 139 KB index, because the index lists every item. The Next.js docs file was 4.4 MB the same day. A fetch tool may cut a long response off, and a model that does get the whole file spends its context on pages it does not need. So we also split the item docs per category into /llms/<category>.txt files. The code sets PART_BYTES = 100_000, and a category that outgrows it continues in -2.txt, -3.txt and so on. Inputs (37 items) comes in four parts, starting with /llms/inputs.txt, and Templates (45 items) in five. The index links every part.
How do I serve a .md twin of every page?
Put a route handler under an internal path and rewrite the public .md URL to it. A dynamic segment takes the whole folder name, so app/docs/[slug].md/ is not a route, and without a rewrite your app/docs/[slug] page receives button.md as the slug. The payoff: an agent that needs one page fetches a few KB instead of a multi-megabyte file.
generateStaticParams prerenders one response per page and dynamicParams = false returns a 404 for any slug you did not list. The canonical Link header is what nextjs.org sends on its own .md pages too (checked October 16, 2026). The rewrite goes in beforeFiles, which runs before Next.js matches pages, so /docs/button.md never reaches the app/docs/[slug] page as a slug called button.md:
Our site runs the same markdown routes for AI agents under three prefixes: /components/:slug\\.md, /templates/:slug\\.md and /blog/:slug\\.md each rewrite to a handler under md/[slug]. This post has one too, at its .md URL.
How do agents find the Markdown version of a page?
Through a link in the HTML head. In generateMetadata, alternates.types renders a <link rel="alternate" type="text/markdown">, which is the link relation the proposal describes:
Relative URLs in metadata need metadataBase in the root layout. The page renders the same doc.body the twin is built from, so the two cannot disagree. headingIds gives each heading an anchor, and fenced code renders through the Code Block with a copy button.
Next.js also answers Accept: text/markdown on its own docs pages with Markdown and sends Vary: Accept (checked October 16, 2026). You can copy that with a has header condition on a second rewrite, but a CDN that caches by URL alone can then hand Markdown to a browser unless every response sends Vary: Accept. We skipped it: separate .md URLs cache cleanly and are easy to paste into a chat.
What are the llms.txt best practices?
Seven rules, all of them in our own setup:
- Generate, never hand-edit. Counts, prices and page lists come from code, so the files ship with each deploy.
- Keep the index a map. One line per page, with notes that say when to read it. Long text belongs in the twins and the full file.
- Split the full file. Per section files under about 100 KB are easier to fetch than one file of several megabytes.
- Leave out drafts. Our loader drops draft posts from the production build, so they never reach the files. In dev they render, and their twins send
X-Robots-Tag: noindex. - Put a canonical on every twin. The
Linkheader keeps the.mdURL from competing with the HTML page in search. - Do not block them. Our
robots.tsdisallows only private paths (account, checkout, admin, API, previews), so/llms.txtand every.mdroute stay crawlable. A disallow would hide them from the agents you made them for. - Never leak private content. Our
llms-full.txtholds props and usage for every item but never the source of a Pro item.
Does anyone actually read llms.txt?
Coding agents and assistants that fetch your URLs while they work can read it. Google does not need it: its AI features documentation (opens in a new tab) says you do not need AI text files or other new machine readable files to appear in AI Overviews or AI Mode (last updated December 10, 2025).
A coding agent told to use your library, or a chat assistant given your URL, gets your real props in one fetch. That is also why the twins matter more than the index. "Read https://wingo-ui.com/components/data-table.md (opens in a new tab), then write an orders table with it" is a prompt that works in any assistant that can fetch a URL.
When an agent can do more than read, an MCP server is the better channel: it can search, return props and source, and check for updates. Our component library MCP guide covers that setup, and the AGENTS.md vs CLAUDE.md comparison covers the rules file that tells agents to use it. If you are setting up the app itself, start with our Next.js component library setup.
How do I check that it works?
With curl, against the dev server or a production build:
Run next build once too: its route list should mark /llms.txt, /llms-full.txt and /docs/md/[slug] as prerendered (static or SSG), not dynamic, which confirms nothing runs per request.
What should you do next?
Ship the index and the twins first: the index is the map, and the twins answer the one-page fetches. Then add llms-full.txt, and split it per section once a file grows past about 100 KB. If your docs show code, start with the free Code Block: npx wingo-ui@latest add code-block. The Markdown renderer used above is part of Pro.
FAQ
Where do I put llms.txt in a Next.js App Router project?
Create app/llms.txt/route.ts with a GET handler that returns the text, and export const dynamic = "force-static" so it is generated at build time. A static public/llms.txt also works, but it goes stale the first time you add a page.
What is the difference between llms.txt and llms-full.txt?
llms.txt is a short index: the site name, a summary and lists of links with one-line notes. llms-full.txt puts the full text of those pages in one file; it is a community convention that the llmstxt.org proposal does not define.
How do I serve a .md version of every page in Next.js?
A dynamic segment takes the whole folder name, so app/docs/[slug].md does not work. Add a route handler at app/docs/md/[slug]/route.ts and a beforeFiles rewrite from /docs/:slug.md to /docs/md/:slug in next.config.ts. Return the Markdown with content-type text/markdown and a canonical Link header that points to the HTML page.
Does Google use llms.txt?
Google says you do not need AI text files or other new machine readable files to appear in AI Overviews or AI Mode (Search Central, last updated December 10, 2025). The files help agents and assistants that fetch your URLs while they work, such as coding agents reading your docs.
What content type should llms.txt use?
The proposal does not mandate one. text/plain; charset=utf-8 is the safe choice for the .txt files because browsers show it inline, and text/markdown; charset=utf-8 fits the .md twins.
- llms.txt
- Next.js
- App Router
- AI agents
- Markdown
- SEO


