How the template works
Every page on this site — including this one — is an object in the DOCS array in content/docs.ts. The dynamic route at app/docs/[slug]/page.tsx renders whatever it finds there, and generateStaticParams pre-builds one static page per entry. The sidebar, search index, and on-this-page rail are all derived from the same array.
There is no MDX pipeline and no CMS: content is typed TypeScript, so your editor autocompletes the shape (DocPage in content/types.ts) and the build fails loudly if an entry is malformed.
npm run dev every page is editable in place. Hover any paragraph, code block, or signature card for a pencil; use the dashed + block, + member, and + section buttons to grow a page; the pencil next to the page title edits the header fields — its Delete button removes the whole page. Every change is written straight back into content/docs.ts.Adding a new page
Append a DocPage to DOCS. The id becomes the URL (/docs/your-id), group decides the sidebar section, and kind renders as the badge next to the nav entry.
{
id: "knockback", // route: /docs/knockback (unique, kebab-case)
title: "KnockbackService",
kind: "module", // "module" | "library" | "api" | "guide"
group: "Combat", // sidebar section, created on demand
version: "1.2.0",
source: "src/server/Knockback.luau",
updated: "2026-07-31",
summary: "One-line description shown under the page title.",
sections: [
{ heading: "Overview", blocks: [{ p: "…" }] },
],
},content/docs.ts on disk, so it only shows up under npm run dev.Adding code to document
Anywhere inside a section's blocks, add a code block. Highlighting is done at build time by Shiki — the same engine VS Code uses — so there's zero client-side JS cost. Luau is mapped onto the Lua grammar.
{
code: {
lang: "luau",
file: "src/shared/Util.luau",
source: `local function lerp(a: number, b: number, t: number): number
return a + (b - a) * t
end`,
},
},For structured API reference (functions, methods, events), use a members block instead — it renders shadcn-styled signature cards with parameter tables. See the SessionService page for a live example.
Reusing this as a template
- Duplicate the repo per project; change
SITEincontent/docs.tsand the metadata inapp/layout.tsx. - The palette lives in the CSS variables in
app/globals.css(:rootand@theme inline) — every component derives from them. - Delete the sample pages in
DOCSand start fresh. - shadcn components live in
components/ui— add more withnpx shadcn@latest add <name>.