Authoring Docs
Write docs as a maintained LLM-wiki - atomic, linked, linted pages in the Fumadocs app, gated by config.docs.
The docs site is apps/docs (a Fumadocs app) gated by config.docs.enabled. It renders Markdown from the repo-root /docs folder (config.docs.contentDir), so the same files appear here and ship into your generated project as AI-agent context (the generated AGENTS.md points coding agents at docs/).
Treat /docs as a maintained LLM-wiki (Karpathy's term): a graph of atomic, cross-linked pages that an AI retrieves from, not a pile of long articles. The rules below merge that discipline with how the docs are delivered (silos, components, scannable style).
Structure: one concept per page
Each page is the smallest useful unit, so a reader, human or AI, lands on exactly the concept they need.
- One concept per page. A focused page is easier to scan and retrieves better than a grab-bag. When a page covers two things, split it (see Restructuring).
- The path is the page's stable key.
payments/creditsis referenced by that path. Pick a clear, stable slug; renaming it breaks every inbound link and any backlink, so redirect when you must change it. - Category is the folder. Related pages share a folder (
payments/,deployment/); a top-level page has no category. Folder = sidebar group = silo section. - Prefer many small linked pages over a few large ones. Splitting and linking beats sprawling.
The cross-reference graph: no orphans
The wiki is the links, not just the pages. A page nobody links to is invisible to a reader navigating by topic and weak for retrieval.
- Every page is reachable. A new page must be linked from at least one related page (and link out to its neighbors), and listed in the silo
meta.json. Cross-reference liberally. - Link instead of re-explaining. Point at the sibling page that owns a concept rather than restating it; that keeps each page atomic and the graph dense.
- Close the loop both ways. When page A references B, make sure B (or its section) also points back where it helps a reader going the other direction.
Health: lint the wiki
After any docs change, run:
pnpm docs:lintIt fails on broken links (a cross-link or <Card href> to a missing page), orphans (a page no other page links to), and missing frontmatter (title/description). Fix every finding before you are done.
Writing style
Pages double as AI-agent context, and readers scan rather than read. Be high-impact, actionable, visual, and dense.
- Summary first. Open with one self-contained paragraph that names the page's subject in full and says what it covers. A retrieved chunk, or a skimming reader, must understand the page from that opener alone.
- Self-contained sections. Each
##/###section leads with a sentence that names its subject, never leaning on "this" or "it" to refer back. An AI retrieves a section out of context, so each must stand alone. - Action first. Lead with the command, config, or steps; defer the background. A reader arrives to act, not to study.
- Visual first. Lead with a table,
<Cards>,<Steps>, or a<Callout>; use prose only for the connective why. - Maximize value per word. Every line earns its place. Cut filler ("simply", "just", "in order to").
- Be concrete. Name the file, key, or command; link a sibling page instead of re-explaining it.
- Complete, not thin. Cover every step the reader needs; never leave them guessing what to do next.
- Clear over clever. Plain words, present tense, active voice.
Frontmatter
Validated against Fumadocs' pageSchema. Unlisted keys are silently dropped:
| Field | Required | Description |
|---|---|---|
title | yes | H1 + sidebar label. |
description | yes | One dense, self-contained sentence: name the subject in full and say what the page covers. Doubles as SEO, the llms.txt blurb, and the retrieval hint, so it is the single highest-value line on the page. |
icon | no | A Lucide PascalCase name shown in the sidebar. |
full | no | true renders the page full-width (no on-this-page TOC). |
---
title: Credits
description: How the numeric credit balance is spent, metered, and granted in platform billing mode.
icon: Coins
---Sidebar order with meta.json
The silo folder's meta.json controls its sidebar. The title labels that silo in the switcher.
{
"title": "My Stack",
"root": true,
"pages": ["index", "---Getting Started---", "quick-start", "..."]
}| Key | Effect |
|---|---|
title | Folder label, shown in the switcher dropdown. |
root: true | Marks the folder a top-level section. Fumadocs builds the switcher from every root folder and isolates the sidebar to the active one. |
pages | Slugs in display order. |
"---Label---" | A pages entry rendered as a section divider. |
"..." | A pages entry that expands to "everything else." |
defaultOpen / collapsible / icon | Optional folder display tweaks (see Fumadocs docs). |
If the boilerplate ships more than one silo, the switcher flips between them with no layout code. A generated project keeps only its own silo.
Components
apps/docs/components/mdx.tsx returns the global component map (getMDXComponents): Fumadocs defaults - <Cards>/<Card>, <Callout> - plus the explicitly added <Steps>/<Step>. Use any of them in any .mdx with no import.
| Component | Use for |
|---|---|
<Cards> / <Card> | Next-step / routing grids. Doubles as graph edges out of the page. |
<Callout> | A genuine warning or prerequisite. |
<Steps> / <Step> | Ordered setup or checklists. |
Register new MDX components in getMDXComponents, never with a per-file import. Because /docs lives outside apps/docs, a bare import inside a docs page fails the build.
Cross-links
- Link to siblings with a silo-prefixed path:
[Payments](/next/payments). - Never use
/docs/...or a bare/payments. - These links resolve within whichever silo the page is viewed in - that is what lets shared backend pages live identically across silos, and they are the edges
docs:lintwalks to find orphans and broken links.
Add a new page
.md / .mdx file under the silo folder in /docs, with title + description frontmatter.pages in the folder's meta.json to place it in the sidebar.pnpm docs:lint (no orphans / broken links / missing frontmatter), then pnpm --filter docs dev (default port 3040) to preview.Restructuring a large page
When a page covers more than one concept, promote it to a collapsible folder of focused sub-pages:
payments/) and move the page into it as index.mdx. This keeps the original /next/payments URL working as the folder landing page, so no redirect is needed..mdx files (plans-and-pricing.mdx, credits.mdx, ...), each one focused concept.meta.json with title, an icon, and pages in display order. The folder renders as a collapsible group; add "defaultOpen": false to ship it collapsed.<Cards> grid linking the sub-pages, so the folder landing page is a hub in the graph rather than a wall of text.// docs/next/payments/meta.json
{ "title": "Billing & Payments", "icon": "CreditCard", "pages": ["plans-and-pricing", "credits", "organizations"] }Renaming or moving a page
A changed URL must never strand a reader or lose its backlinks.
- No broken links: after any move or rename, search the whole
/docstree for the old path and update every reference - sibling pages,<Cards>, andmeta.json(grep -rn "/next/old-path" docs).pnpm docs:lintcatches any you miss. - Redirect the old URL: add a permanent (308) redirect to the closest new page so SEO and backlinks transfer. Next applies
basePathautomatically, so list the silo-prefixed path:
// apps/docs/next.config.ts
const nextConfig: NextConfig = {
// ...existing config
async redirects() {
return [{ source: "/next/old-page", destination: "/next/section/new-page", permanent: true }];
},
};index.mdx keeps the original URL, so it needs no redirect. Only redirect URLs that actually change.Frequently asked questions
Why an LLM-wiki and not just docs? These pages ship into every generated project as AI-agent context. An atomic, linked, linted graph means a coding agent retrieves the one relevant page with its neighbors, instead of a long article where the answer is buried.
Why silo-prefixed links instead of [[wikilinks]]?
The docs use silo-prefixed Markdown links because the same shared page renders in every framework silo and the link must resolve per-silo. The graph and its lint are the point, not the syntax.
Where do I change which folder gets read?
config.docs.contentDir drives the source directory (this repo reads top-level ../../docs; generated projects default to content/docs). See Configuration.
Why doesn't my page show in the sidebar?
A page only appears once its slug is listed in the silo meta.json pages array - or matched by a "..." catch-all entry.
Customization
Rebrand the boilerplate by editing branding, logo, social, and SEO fields in @repo/config plus your English i18n copy - one source of truth per surface.
Troubleshooting
Fix common development and deployment errors fast, from missing environment variables and background jobs not firing locally to unauthenticated server fetches.