- Introduction
- Pitch
- Hogwarts
- Live Demo
- MVP
- Roadmap
- Launch Sprint
- PRD
- Get Started
- Localhost
- Architecture
- Structure
- Pattern
- Page
- Layout
- Content
- Types
- Config
- Actions
- Queries
- Authorization
- Validation
- Form
- Table
- Detail
- Card
- Util
- Hooks
- List Params
- Views
- README.md
- ISSUE.md
- Technology Stack
- Database
- File
- CDN Assets
- Entry Points
- Dashboard
- Authentication
- Credentials
- OAuth
- Flow Diagrams
- Multi-Tenancy
- Offline
- Onboarding
- Onboarding Videos
- Add Values
- Admission
- Application
- Attendance
- Compliance
- Profile
- Exams
- Exam Wizard
- Timetable
- Classrooms
- Notifications
- Conference
- LMS (Lumos)
- Finance
- Fee Management
- Invoice
- Wallet
- Salary
- Payroll
- Timesheet
- Expenses
- Budget
- Receipt
- Accounts
- Banking
- Reports
- Dashboard
- Permissions
- Messages
- Integration Flow
- Provision
- AI Document Processing
- Document Intelligence
- Internationalization
- Translation
- Translation Guide
- Icons
- Docs Factory
- Inspiration
- Listings
- Teachers
- Students
- Catalog
- Library
- Contributing
- Code of conduct
- GitHub Workflow
- Database Seeds
- Database Safety
- Test Accounts
- Playwright
- Prettier
- Block Rebound
The docs system is wired with fumadocs-mdx: one flat MDX directory per language, a single meta.json for ordering, and a catch-all dynamic route that statically generates every page.
Architecture
content/
├── docs-en/ # 88 .mdx files, FLAT (no subdirs)
│ ├── meta.json # Single nav config for the whole sidebar
│ ├── index.mdx # /en/docs landing
│ └── *.mdx
└── docs-ar/ # 16 .mdx files, FLAT
├── meta.json
└── *.mdx
source.config.ts # defineDocs({ dir: "content/docs-en" }) + docsArabic
src/app/[lang]/(root)/docs/
├── [[...slug]]/page.tsx # Catch-all renderer, source-aware per locale
└── layout.tsx # Sidebar + TOC layout
src/components/docs/
├── docs-sidebar.tsx # Renders fumadocs pageTree
└── toc.tsx # Table of contents
src/mdx-components.tsx # Component overrides (incl. runtime shiki)
src/lib/detect-npm-command.ts # Package-manager-tab logic
There is no getting-started/ or components/ subdirectory. Files are flat; ordering is controlled entirely by meta.json.
Runtime shiki (the OOM story)
Build-time syntax highlighting via rehype-pretty-code was disabled because it OOM'd Vercel Hobby's 8 GB heap when compiling 111 MDX files with two themes (light + dark). After 12 deploy attempts we moved highlighting to render time.
// source.config.ts
export default defineConfig({
mdxOptions: {
rehypeCodeOptions: false, // build-time shiki off
},
})Code blocks now highlight in the browser via fumadocs-ui/DynamicCodeBlock:
- Lazy
import("shiki")only when a code block enters the viewport - JS regex engine, no oniguruma WASM
- See
src/mdx-components.tsxfor thepre/codeoverrides - See
src/lib/detect-npm-command.tsfor the package-manager tab logic that previously rode along with the shiki transformer
Trade-off: a ~100-200 ms shiki hydration flash on the first code block visible. Acceptable for free-tier hosting; the alternative was paying for a build with more heap.
Add a doc
-
Create the MDX file in the right language directory:
--- title: "Your Page Title" description: "Brief description for SEO and previews" --- Your content here. ## Section 1 ... -
Add the slug to
meta.json(English example):// content/docs-en/meta.json { "title": "Documentation", "pages": ["index", "your-new-page", "..."] } -
Optional — import React components inline:
import { Button } from "@/components/ui/button" <Button>Click me</Button> -
Build and test:
pnpm fumadocs-mdx pnpm dev
Frontmatter
Required: title, description. Optional: links, publishedAt, updatedAt, author, tags.
---
title: "Page Title"
description: "Description"
links:
doc: "https://docs.example.com"
api: "https://api.example.com"
publishedAt: 2024-01-01
updatedAt: 2024-01-15
author: "Jane Doe"
tags: ["react", "tutorial"]
---MDX features
Code blocks (typescript, bash, etc.), Markdown tables, and custom callouts:
<Callout type="info">An informational callout.</Callout>
<Callout type="warning">A warning.</Callout>Navigation config
A single meta.json per language sits at the directory root and lists every page in render order:
// content/docs-en/meta.json
{
"title": "Documentation",
"pages": ["index", "get-started", "architecture", "..."]
}There are no nested section meta.json files — the sidebar groups visually, but storage is flat.
Customization
Custom MDX components — register in src/mdx-components.tsx:
export const mdxComponents = {
h1: ({ children }) => <h1 className="custom-heading">{children}</h1>,
Button,
Alert,
CodeBlock,
}Sidebar sections — edit src/components/docs/docs-sidebar.tsx:
const TOP_LEVEL_SECTIONS = [
{ name: "Get Started", href: "/docs" },
{ name: "Components", href: "/docs/components" },
]Styling — container-wrapper, Tailwind prose classes, CSS variables in globals.css, dark mode via next-themes.
Conventions
- File names: kebab-case (
my-doc-page.mdx,authentication-setup.mdx); language landing isindex.mdx. - Start with a single H1 matching the title; logical heading hierarchy; focused sections; code examples.
- Title under 60 chars; description under 160 chars.
- Images: store in
public/docs/, descriptivealt, prefer WebP. - Internal links: use
/docs/<page-name>so the locale segment is added by the router.
Troubleshooting
Build errors — regenerate the MDX index, clear cache:
pnpm fumadocs-mdx
rm -rf .next
pnpm buildMissing frontmatter — every MDX file must have title and description.
Import errors — use absolute aliases (@/components/ui/component).
Navigation not updating — make sure the slug is listed in meta.json for the right language → run pnpm fumadocs-mdx → restart dev.
OOM on Vercel — never re-enable rehype-pretty-code. If you need to, you must also drop one of the themes or shrink the docs surface; otherwise the build will fail at 8 GB heap. See the runtime shiki section above.