web-meta-framework-docusaurus · git:20260906.ae0cc61 · 2026-09-06 · sha256 a76f0202f917240a
web-meta-framework-docusaurus git:20260906.ae0cc61A
Immutable. This exact content is served forever at /api/v1/blob/a76f0202f917240a.
---
name: web-meta-framework-docusaurus
description: Docusaurus 3.x documentation framework — site configuration, docs/blog plugins, sidebars, versioning, MDX, swizzling, and deployment
---
# Docusaurus Patterns
> **Quick Guide:** Docusaurus 3.x is a React-powered static site generator for documentation. Everything
> is configured in `docusaurus.config.js` (ESM), and `@docusaurus/preset-classic` bundles docs, blog,
> pages, sitemap and theme in one entry. Sidebars are autogenerated from the filesystem, ordered by
> `sidebar_position` front matter and `_category_.json`. Theme components are customised by swizzling.
> Content is MDX v3, which is stricter than Markdown. `docusaurus docs:version` snapshots the whole
> `docs/` tree, and
> `docusaurus build` emits a static `build/` directory.
**Detailed Resources:**
- [examples/core.md](examples/core.md) — full config, autogenerated and manual sidebars, multi-instance docs, docs-only mode, custom pages, navbar item types
- [examples/content.md](examples/content.md) — MDX strictness, admonitions, tabs, code blocks, assets, blog plugin and authors, remark/rehype, doc links
- [examples/customization.md](examples/customization.md) — swizzle safety, Infima CSS variables, versioning, i18n, search, deployment, Mermaid
- [reference.md](reference.md) — CLI commands, front matter fields, plugin and theme option tables, import aliases
---
<critical_requirements>
## Before writing Docusaurus code
**Keep all site configuration in `docusaurus.config.js` (or `.ts`).** It is the single entry point the
build reads, and config split across files has no mechanism to be merged.
**Start from `@docusaurus/preset-classic`.** It wires docs, blog, pages, sitemap and the theme together;
decompose into individual plugins only when you need something the preset cannot express, such as a
second docs instance.
**Swizzle with `--wrap` unless the change genuinely needs the component's internals.** A wrapped
component keeps receiving upstream fixes; an ejected one is a snapshot you now own.
**Order autogenerated sidebars with `sidebar_position` front matter and `_category_.json`.** The
filesystem drives routing and sidebar structure, so ordering stays next to the content it orders.
**Give each doc set its own plugin instance when it needs its own versioning.** One instance holding
both versioned and unversioned docs has no way to keep them apart.
</critical_requirements>
---
**Auto-detection:** Docusaurus, docusaurus.config.js, docusaurus.config.ts, @docusaurus/preset-classic, @docusaurus/core, sidebars.js, docs:version, docusaurus build, docusaurus start, docusaurus deploy, docusaurus swizzle, MDX, _category_.json, sidebar_position, @site, @theme, @theme-original, plugin-content-docs, plugin-content-blog
**Applies to:**
- `docusaurus.config.js` — site metadata, presets, plugins, `themeConfig`, navbar, footer
- Sidebars: autogenerated, manual, multiple, and custom item generators
- Docs versioning and version banners
- Theme customization via swizzling and Infima CSS variables
- MDX content: admonitions, tabs, code block features, assets, heading anchors
- Custom pages in `src/pages/`, in React or MDX
- Blog plugin configuration and `authors.yml`
- Search wiring, i18n, and deploying the static build
**Handled elsewhere:**
- React component authoring itself — this skill covers the Docusaurus APIs a page or swizzled component calls, not the component model underneath.
- Request-time behaviour — `docusaurus build` emits static files, so anything needing a server at request time lives outside the site.
- Content sourced from a CMS or database — the plugins read files from disk at build time.
- The visual language beyond the Infima variables the theme exposes — those variables are the seam, and what you set them to is a design decision.
- The prose itself — this skill settles how a doc is wired into the site, not what it says.
---
<philosophy>
Docusaurus is an **opinionated documentation framework that trades flexibility for convention**. It
decides routing (filesystem), content format (MDX) and structure (docs, blog, pages) so the work left is
writing.
1. **Convention over configuration** — the filesystem drives routing and sidebar generation. Fighting it
is fighting the framework.
2. **Preset first** — `preset-classic` is the common plugin set already wired together; individual
plugins are for setups it cannot express.
3. **Content is data** — front matter is the metadata layer. `sidebar_position`, `slug`, `tags` and
`custom_edit_url` live in the document rather than in a separate index.
4. **Swizzle rather than fork** — wrapping preserves upstream compatibility; ejecting produces a
snapshot with your name on it.
5. **Static output** — there is no server runtime, no request-time rendering and no API routes.
</philosophy>
---
<decision_framework>
**Which sidebar strategy?** Autogenerated, ordered by `sidebar_position` and `_category_.json`, is right
for almost everything — including large sites, as long as the sections map onto directories. A manual
sidebar in `sidebars.js` earns its maintenance only when the navigation has to group documents the
filesystem keeps apart, and the choice is not all-or-nothing: one sidebar array can hold an
`autogenerated` item alongside hand-written entries. Independent doc sets (an API reference beside a
guide) want separate plugin instances, each with its own sidebar, rather than either strategy stretched.
**Wrap, eject, or neither?** Adding content around a component is `--wrap`. Changing colours or spacing
is neither — set the Infima CSS variables in `custom.css`. Changing internal logic means checking the
component's safety level first: "Safe" makes ejecting acceptable, "Unsafe" means wrap or find another
route, and "Forbidden" means the component is not swizzlable at all.
**Which content format?** A documentation article or blog post is `.md` or `.mdx` in `docs/` or `blog/`.
A standalone page that is mostly prose is `.mdx` in `src/pages/`; one that is mostly interactive is
`.tsx` in the same place. Embedding React components in documentation means `.mdx` with imports.
**Version, or not?** Versioning copies the entire `docs/` tree, so each version costs its own build time
and output size. Cut a version when the documentation genuinely differs between releases — not on every
release.
</decision_framework>
---
<patterns>
## Core patterns
### Pattern 1: `docusaurus.config.js`
One ESM file: site metadata, the preset (which brings the plugins and theme), and `themeConfig` for
navbar, footer and theme behaviour.
```javascript
export default {
title: "My Docs",
url: "https://docs.example.com",
baseUrl: "/",
onBrokenLinks: "throw",
presets: [
[
"@docusaurus/preset-classic",
{
docs: {
sidebarPath: "./sidebars.js",
editUrl: "...",
showLastUpdateTime: true,
},
blog: { showReadingTime: true },
theme: { customCss: ["./src/css/custom.css"] },
},
],
],
themeConfig: {
navbar: {
/* ... */
},
footer: {
/* ... */
},
},
};
```
`onBrokenLinks: "throw"` is what makes a broken internal link fail the build rather than ship.
Full code: [examples/core.md](examples/core.md)
### Pattern 2: Sidebars
An autogenerated sidebar reads the filesystem; ordering and labels come from front matter and
`_category_.json` beside the content.
```javascript
// sidebars.js
export default { docs: [{ type: "autogenerated", dirName: "." }] };
```
```json
// docs/guides/_category_.json
{
"label": "Guides",
"position": 2,
"collapsed": false,
"link": { "type": "generated-index" }
}
```
Without `sidebar_position`, items sort alphabetically by filename. Number prefixes (`01-intro.md`) are
the alternative and are stripped from the URL slug — pick one mechanism, not both.
Full code: [examples/core.md](examples/core.md)
### Pattern 3: Swizzling theme components
A wrapper renders the original and adds around it. The import is `@theme-original/` — `@theme/` would
resolve to the wrapper itself.
```bash
npx docusaurus swizzle --list
npx docusaurus swizzle @docusaurus/theme-classic Footer -- --wrap
```
```jsx
// src/theme/Footer/index.js
import Footer from "@theme-original/Footer";
export default function FooterWrapper(props) {
return (
<>
<Footer {...props} />
<div className="custom-banner">Custom content below footer</div>
</>
);
}
```
Full code: [examples/customization.md](examples/customization.md)
### Pattern 4: MDX content
MDX v3 is Markdown with JSX. Admonitions, tabs and code block metadata are the Docusaurus-specific
additions.
```mdx
---
title: My Document
sidebar_position: 1
---
import Tabs from "@theme/Tabs";
import TabItem from "@theme/TabItem";
:::tip[Pro Tip]
`note`, `tip`, `info`, `warning` and `danger`; the bracket sets a custom title.
:::
<Tabs groupId="package-manager">
<TabItem value="npm" label="npm" default>
...
</TabItem>
</Tabs>
```
MDX v3 is stricter than Markdown: an unclosed HTML tag, a bare `{`, or a `<` used as a comparison in
prose all fail the build.
Full code: [examples/content.md](examples/content.md)
### Pattern 5: Versioning
`docs:version` snapshots the whole `docs/` directory. What remains in `docs/` becomes the unreleased
`current` version.
```bash
npx docusaurus docs:version 1.0.0
```
```javascript
docs: {
lastVersion: "current",
versions: {
current: { label: "2.0.0-beta", path: "next", banner: "unreleased" },
"1.0.0": { label: "1.0.0", path: "1.0.0", banner: "none" },
},
onlyIncludeVersions: ["current", "1.0.0"],
}
```
Versions are full copies rather than diffs, so `onlyIncludeVersions` is how you keep development builds
fast.
Full code: [examples/customization.md](examples/customization.md)
</patterns>
---
<red_flags>
## Red flags
**Breaks at runtime:**
- A bare `{` or an unclosed tag in MDX — MDX v3 fails the build; escape with `\{` and `\<`, or use a code fence.
- `@theme/ComponentName` imported inside a swizzle wrapper — the wrapper resolves to itself, and the import loops.
- `baseUrl` without a leading and trailing `/` — asset resolution breaks.
- `_category_.json` written with `name` or `order` instead of `label` and `position` — the fields are ignored and the category falls back to defaults.
- A direct `@docusaurus/`-package import inside MDX — reach for the `@theme/` and `@site/` aliases instead.
- Docs-only mode configured with `routeBasePath: "/"` but `blog` left enabled, or `src/pages/index.js` left in place — the routes collide.
**Surprising behaviour:**
- `onBrokenLinks` set to `"ignore"` or `"log"` ships dead links; `"throw"` is what a production config wants, with `onBrokenMarkdownLinks` at least at `"warn"`.
- Ejecting where wrapping would have done means upstream fixes stop arriving for that component.
- A hand-maintained `sidebars.js` where autogeneration would work drifts out of step with the docs it describes.
- No `editUrl` means no "Edit this page" link, and no `showLastUpdateTime` means a reader cannot tell how stale a page is.
- Changes to `docusaurus.config.js` are not hot-reloaded — restart the dev server.
- With `format: "detect"` configured, only `.mdx` files process JSX; the default mode processes both extensions.
- Static assets in `static/` are served from the site root rather than from `baseUrl` — use `require()` or `useBaseUrl()` for a path-safe reference.
- Blog authors live in `blog/authors.yml`, not in the site config.
- The `slug` front matter field overrides the URL derived from the filename, which is how a rename keeps its URL.
- A page in `src/pages/` takes its route from its file path — `src/pages/support.tsx` becomes `/support`.
- `<Tabs>` instances do not sync until they share a `groupId`.
</red_flags>