The consumer repo holds only content + docs.config.ts. The kit owns Vite and React Router
config; do not add vite.config.ts or react-router.config.ts unless overriding (see
reference/config.md).
lobe-ui itself is the reference consumer: docs.config.ts, docs/, src/*/index.mdx,
src/*/demos/.
1. Repo layout
<repo>/
├── docs.config.ts # defineDocsConfig({...}) — required
├── package.json # lobedocs scripts
├── docs/
│ ├── index.mdx # "/" — required (frontmatter feeds SEO even with a custom homePage)
│ ├── changelog.mdx # "/changelog" — optional; else root CHANGELOG.md is used
│ └── home/home.tsx # optional custom landing page (config.homePage)
├── src/
│ └── Button/
│ ├── index.ts # barrel export — <Api> resolves props through it
│ ├── Button.tsx
│ ├── type.ts # props with JSDoc
│ ├── index.mdx # -> /components/button
│ └── demos/
│ ├── index.tsx # default-export component
│ └── Variant.tsx
├── public/ # static assets served at "/" (favicons, og images)
└── CHANGELOG.md
Hard rules the compiler enforces:
- Only files named
index.mdxunder anatomDirsroot become component pages (index.mdandREADME.mdare ignored). - Demo files must live under
src/**/demos/**— standalone/~demos/:idroutes andisolateddemos are globbed from that path only. - Other
docs/**/*.mdxfiles are not discovered unless listed inpublicDocs.
2. Install and scripts
pnpm add -D @lobehub/docs-kit @react-router/dev@8.2.0 react-router@8.2.0 vite@8.1.4 tsx
pnpm add react react-dom # ^19
{
"scripts": {
"docs:dev": "lobedocs dev",
"docs:build": "lobedocs build",
"postinstall": "lobedocs typegen"
}
}
lobedocs build writes the static site to dist/ (Pagefind search, sitemap, /llms.txt,
/skills.md are generated automatically). Node >= 22.22.
3. Minimal docs.config.ts
export default {
atomDirs: [{ dir: 'src' }],
title: 'Lobe Editor',
description: 'One sentence describing the library.',
siteUrl: 'https://editor.lobehub.com',
alias: { '@': 'src', '@lobehub/editor': 'src' }, // let demos import the package from source
themeConfig: {
apiHeader: {
packageName: '@lobehub/editor', // defaults to @lobehub/ui — always set it
github: 'https://github.com/lobehub/lobe-editor',
},
socialLinks: [
{ href: 'https://github.com/lobehub/lobe-editor', icon: 'github', label: 'GitHub' },
],
},
};
The package's only stable JS entry is
@lobehub/docs-kit/react-router-config; there is no root export fordefineDocsConfig. It is an identity function, so export a plain object. (lobe-ui imports it from./packages/docs-kit/src/configonly because it is the workspace.)
Restart lobedocs dev after editing docs.config.ts — config is cached per process.
All fields (multiple atomDirs, subType, homePage, publicDocs, navItems, giscus,
analytics, legacyRedirects) are in reference/config.md.
4. Writing a component page (src/<Name>/index.mdx)
Template:
---
title: Button
description: Button triggers an action. Supports variants, sizes, loading and icon slots.
category: General
order: -1
---
import Basic from './demos/index.tsx?demo';
import Variants from './demos/Variant.tsx?demo';
## Introduction
One or two paragraphs: what it is, when to use it, when to use something else.
## Basic Usage
<Demo of={Basic} title="Basic usage" layout="bare" />
## Variants
Explain the dimension first, then show it.
<Demo of={Variants} title="Variants" />
## API
<Api name="Button" />
Additionally, Button supports all props of antd's Button except `icon`.
Frontmatter
| Field | Required | Effect |
|---|---|---|
title |
yes | H1, <title>, sidebar label. If PascalCase, the header shows import { Title } from '<packageName>' — match the export name |
description |
yes | Subtitle, meta/OG description, search, llms.txt. One or two plain sentences, no markdown |
category |
yes (component) | Sidebar group inside the section. Reuse existing names; General sorts first |
order |
no | Number, lower first within category; unset sorts last, then by title |
status |
no | stable | beta | experimental | deprecated |
since |
no | Version string |
route |
no | Override the URL; component pages must stay under /components/ |
Anything else (group, nav, apiHeader, dumi's hero) is ignored — remove it.
URL is derived from the folder: src/Button/index.mdx → /components/button,
src/base-ui/Select/index.mdx → /components/base-ui/select (each segment kebab-cased).
Headings
The page already renders title as the H1. Start body sections at ##; a stray # is
downgraded to H2. Every heading gets an anchor and appears in the outline and llms.txt, so use
short, stable, noun-style headings (## Variants, ## Controlled, ## API).
Demos
import Basic from './demos/index.tsx?demo';
<Demo of={Basic} title="Basic usage" description="Optional caption." layout="center" />
| Prop | Default | Use |
|---|---|---|
of |
— | Required. The ?demo import (without ?demo you get the raw component and it fails) |
title / description |
— | Caption above the demo |
layout |
'default' |
'bare' no padding/frame (playgrounds, full-width); 'center' centered small elements |
isolated |
false |
Render in an iframe — for fixed/portal/global-CSS demos that would leak into the page |
editable |
true |
Set false if the demo uses dynamic import(), workers, or odd local deps |
height |
— | Frame height, e.g. for isolated demos |
Demo file rules:
- Exactly one
export defaultcomponent; import the library by its package name (import { Button } from '@lobehub/editor') so the shown source is copy-pasteable — thealiasmaps it back tosrc/. - One idea per demo; name files after the idea (
Variant.tsx,Controlled.tsx),index.tsxfor the basic one. - Keep it self-contained: no fetching, no reliance on site state; mock data inline.
- Interactive playgrounds (lobe-ui style) use
StoryBook+useControlsfrom@lobehub/ui/storybookwithlayout="bare".
API table
<Api name="Button" /> <!-- resolves via nearest index.ts barrel -->
<Api name="ChatItem" from=".." /> <!-- module path relative to this index.mdx -->
Only static name and from are allowed (data is compiler-owned; migrationKey in old
lobe-ui pages is inert). Props are extracted from TypeScript, so the quality of the table is the
quality of your types:
export interface ButtonProps {
/**
* Adds a frosted-glass background.
* @default false
*/
glass?: boolean;
/** @deprecated Use `variant="filled"` instead. */
filled?: boolean;
}
- The JSDoc body is the description; supported tags:
@default,@deprecated,@since. @defaultmust match the runtime destructuring default, or the build fails.any/unknownprops fail the build — type them.- Props starting with
_are hidden; inherited React/HTML attributes collapse into a footnote.
Other MDX content
- Globals available without import: only
DemoandApi. Import anything else explicitly (e.g.import { Alert } from '@lobehub/ui'). - GFM is on (tables, task lists, strikethrough). No
:::callout syntax. - Code fences are highlighted only — never executed. dumi's
```tsx | puremeta is harmless but meaningless; plain```tsxis preferred for new pages. .mdfiles can be imported as components:import Changelog from '../CHANGELOG.md'.
5. Guides, home, changelog
- Guide pages: add
docs/<path>.mdx(frontmattertitle+description) and list it inpublicDocs: ['docs/<path>.mdx']→ served at/<path>. Guides get no sidebar entry; link to them viathemeConfig.navItems. - Home:
docs/index.mdxneeds frontmatter. WithouthomePage, the default home is built fromthemeConfig.home(hero, features, install). WithhomePage: './docs/home/home.tsx', that module's default export renders instead and receives{ description: string; getStartedPathname: string }. - Changelog: either
docs/changelog.mdximporting../CHANGELOG.md, or just a rootCHANGELOG.md(no frontmatter needed).
6. Workflow for adding or migrating a page
- [ ] index.mdx exists at src/<Name>/index.mdx with title, description, category
- [ ] every <Demo of={X}> has a matching `import X from './demos/….tsx?demo'`
- [ ] demos default-export and import the package by name
- [ ] <Api name> matches an exported component; props typed with JSDoc
- [ ] dumi leftovers removed: <code src=…>, `group:`, `nav:`, `hero:`, `apiHeader:` frontmatter
- [ ] `lobedocs dev`, open the page, check demos + API table render
- [ ] `lobedocs build` passes (it runs all audits)
When the build fails, the error lists the offending file and rule; see reference/validations.md for every rule and the fix.
Additional resources
- reference/config.md — every
docs.config.tsfield, multi-packageatomDirs, sections/navigation, React Router overrides, tsconfig for typegen - reference/validations.md — what fails
lobedocs buildand why