Using @lobehub/docs-kit
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.mdx under an atomDirs root become component pages
(index.md and README.md are ignored).
- Demo files must live under
src/**/demos/** — standalone /~demos/:id routes and
isolated demos are globbed from that path only.
- Other
docs/**/*.mdx files are not discovered unless listed in publicDocs.
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 for defineDocsConfig. It is an identity function, so export a plain object.
(lobe-ui imports it from ./packages/docs-kit/src/config only 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 Variants from './demos/Variant.tsx?demo';
import Basic from './demos/index.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 |
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 default component; import the library by its package name
(import { Button } from '@lobehub/editor') so the shown source is copy-pasteable — the
alias maps it back to src/.
- One idea per demo; name files after the idea (
Variant.tsx, Controlled.tsx), index.tsx
for the basic one.
- Keep it self-contained: no fetching, no reliance on site state; mock data inline.
- Interactive playgrounds (lobe-ui style) use
StoryBook + useControls from
@lobehub/ui/storybook with layout="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.
@default must match the runtime destructuring default, or the build fails.
any / unknown props 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
Demo and Api. 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 | pure meta is
harmless but meaningless; plain ```tsx is preferred for new pages.
.md files can be imported as components: import Changelog from '../CHANGELOG.md'.
5. Guides, home, changelog
- Guide pages: add
docs/<path>.mdx (frontmatter title + description) and list it in
publicDocs: ['docs/<path>.mdx'] → served at /<path>. Guides get no sidebar entry; link to
them via themeConfig.navItems.
- Home:
docs/index.mdx needs frontmatter. Without homePage, the default home is built
from themeConfig.home (hero, features, install). With homePage: './docs/home/home.tsx',
that module's default export renders instead and receives
{ description: string; getStartedPathname: string }.
- Changelog: either
docs/changelog.mdx importing ../CHANGELOG.md, or just a root
CHANGELOG.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