---
name: web-meta-framework-docusaurus
description: Docusaurus 3.x documentation framework — site configuration, docs/blog plugins, sidebars, versioning, MDX, swizzling, and deployment
---

# Docusaurus

> **Quick Guide:** Docusaurus 3.x is a React-powered static site generator for documentation. Configure everything in `docusaurus.config.js` (ESM). Use `@docusaurus/preset-classic` for docs + blog + pages + sitemap in one preset. Sidebars can be fully autogenerated from filesystem structure using `_category_.json` and front matter `sidebar_position`. Customize theme components via swizzling (prefer `--wrap` over `--eject`). MDX is the default content format — use front matter for metadata, admonitions for callouts, and import React components directly in `.mdx` files. Version docs with `docusaurus docs:version`. Deploy the `build/` output to any static host.

---

<critical_requirements>

## CRITICAL: Before Using This Skill

> **All code must follow project conventions in CLAUDE.md**

**(You MUST use `docusaurus.config.js` (or `.ts`) as the single source of truth for all site configuration — never scatter config across multiple files)**

**(You MUST use `@docusaurus/preset-classic` unless you have a specific reason to configure plugins individually — the preset bundles docs, blog, pages, sitemap, and theme)**

**(You MUST prefer `--wrap` over `--eject` when swizzling — wrapping preserves upstream updates, ejecting creates a maintenance burden)**

**(You MUST use front matter `sidebar_position` and `_category_.json` for sidebar ordering in autogenerated sidebars — do not fight the filesystem-driven convention)**

**(You MUST NOT mix versioned and unversioned docs in the same plugin instance — use separate plugin instances for different doc sets)**

</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

**When to use:**

- Configuring `docusaurus.config.js` (site metadata, presets, plugins, theme, navbar, footer)
- Setting up or modifying sidebar structure (autogenerated or manual)
- Adding versioned documentation
- Customizing theme components via swizzling
- Writing MDX content with Docusaurus-specific features (admonitions, tabs, code blocks)
- Creating custom pages with React components
- Configuring the blog plugin
- Setting up search (Algolia DocSearch or local)
- Deploying Docusaurus to static hosting
- Configuring i18n / localization

**When NOT to use:**

- General React component patterns (Docusaurus uses React internally but this skill covers Docusaurus APIs, not React fundamentals)
- CSS/styling approaches not specific to Docusaurus theming (general CSS patterns are a separate concern)
- Git hooks, linting, or formatting setup (separate from documentation framework concerns)
- Content that belongs in the docs themselves, not the site framework
- **VitePress** — Vue-based, different config format and plugin system
- **Nextra** — Next.js-based, uses `_meta.json` not `_category_.json`, different routing model
- **Starlight** — Astro-based, uses `astro.config.mjs` and content collections, different architecture entirely

---

## Examples

- [Core Configuration & Sidebars](examples/core.md) — docusaurus.config.js, preset-classic, sidebars, front matter, custom pages
- [MDX & Content](examples/content.md) — MDX features, admonitions, tabs, code blocks, assets, blog plugin
- [Customization & Deployment](examples/customization.md) — Swizzling, CSS variables, versioning, i18n, search, deployment

**Other resources:**

- [Quick Reference](reference.md) — CLI commands, front matter fields, config option tables

---

<philosophy>

## Philosophy

Docusaurus is an **opinionated documentation framework** that trades flexibility for convention. It makes strong decisions about routing (filesystem-based), content format (MDX), and structure (docs + blog + pages) so you can focus on writing content rather than building infrastructure.

**Core principles:**

1. **Convention over configuration** — filesystem structure drives routing and sidebar generation; fight this and you fight the framework
2. **Preset-first** — `preset-classic` bundles the common plugin set; only decompose into individual plugins when you need multiple docs instances or unusual setups
3. **Content as data** — front matter is the metadata layer; `sidebar_position`, `slug`, `tags`, `custom_edit_url` all live in the document, not in external config
4. **Swizzle, don't fork** — customize theme components via the swizzle CLI; wrapping preserves upstream compatibility, ejecting creates a snapshot you must maintain
5. **Static output** — `docusaurus build` produces a static site; there is no server runtime, no SSR in production, no API routes

</philosophy>

---

<patterns>

## Core Patterns

### Pattern 1: docusaurus.config.js Structure

The config file is the single entry point. It uses ESM (`export default`) and configures site metadata, presets (which bundle plugins + theme), and theme-level settings like navbar and footer.

```javascript
// docusaurus.config.js — minimal production setup
export default {
  title: "My Docs",
  tagline: "Documentation for My Project",
  url: "https://docs.example.com",
  baseUrl: "/",
  onBrokenLinks: "throw",
  onBrokenMarkdownLinks: "throw",
  favicon: "img/favicon.ico",

  presets: [
    [
      "@docusaurus/preset-classic",
      {
        docs: {
          sidebarPath: "./sidebars.js",
          editUrl: "https://github.com/my-org/my-repo/edit/main/docs-site/",
          showLastUpdateTime: true,
        },
        blog: { showReadingTime: true },
        theme: { customCss: ["./src/css/custom.css"] },
      },
    ],
  ],

  themeConfig: {
    navbar: {
      title: "My Docs",
      items: [
        /* nav items */
      ],
    },
    footer: {
      style: "dark",
      links: [
        /* footer columns */
      ],
    },
    docs: { sidebar: { hideable: true, autoCollapseCategories: true } },
  },
};
```

**Key gotcha:** `onBrokenLinks: 'throw'` is essential for production — it fails the build on broken internal links rather than silently deploying dead links.

> **Full example:** See [examples/core.md](examples/core.md) for complete config with navbar, footer, and multi-instance docs setup.

---

### Pattern 2: Sidebar Configuration

Docusaurus offers two sidebar strategies. **Autogenerated** sidebars derive structure from the filesystem and are the default recommendation. **Manual** sidebars give full control but require maintenance.

```javascript
// sidebars.js — autogenerated (recommended)
export default {
  docs: [{ type: "autogenerated", dirName: "." }],
};
```

Control ordering and labels via front matter and `_category_.json`:

```markdown
---
sidebar_position: 3
sidebar_label: Quick Start
---
```

```json
// docs/guides/_category_.json
{
  "label": "Guides",
  "position": 2,
  "collapsible": true,
  "collapsed": false,
  "link": { "type": "generated-index", "title": "All Guides" }
}
```

**Key gotcha:** Without `sidebar_position`, items sort alphabetically by filename. Use number prefixes (`01-intro.md`) or front matter — but not both, as number prefixes are stripped from the URL slug.

> **Full example:** See [examples/core.md](examples/core.md#sidebar-patterns) for manual sidebars, custom sidebar items generator, and multi-sidebar setups.

---

### Pattern 3: Swizzling Theme Components

Swizzling lets you customize any theme component. **Wrapping** adds behavior around the original; **ejecting** gives you a full copy to modify.

```bash
# List all swizzlable components
npx docusaurus swizzle --list

# Wrap a component (SAFE — preserves upstream updates)
npx docusaurus swizzle @docusaurus/theme-classic Footer -- --wrap

# Eject a component (DANGEROUS — you own the snapshot)
npx docusaurus swizzle @docusaurus/theme-classic Footer -- --eject
```

```jsx
// src/theme/Footer/index.js — wrapping example
import React from "react";
import Footer from "@theme-original/Footer";

export default function FooterWrapper(props) {
  return (
    <>
      <Footer {...props} />
      <div className="custom-banner">Custom content below footer</div>
    </>
  );
}
```

**Key gotcha:** The `@theme-original/` import is critical in wrappers — it references the original component. Using `@theme/` would create an infinite loop since your wrapper IS the `@theme/Footer`.

> **Full example:** See [examples/customization.md](examples/customization.md) for swizzling safety levels, common swizzle targets, and CSS variable theming.

---

### Pattern 4: MDX Content Features

Docusaurus uses MDX v3 — Markdown with embedded JSX. Key Docusaurus-specific features include admonitions, tabs, and code blocks with metadata.

````mdx
---
title: My Document
description: SEO description for this page
sidebar_position: 1
tags: [getting-started, tutorial]
---

import Tabs from "@theme/Tabs";
import TabItem from "@theme/TabItem";

:::tip[Pro Tip]
Admonitions support `note`, `tip`, `info`, `warning`, `danger` types.
The bracket syntax `:::tip[Custom Title]` sets the title.
:::

<Tabs>
  <TabItem value="npm" label="npm" default>
    ```bash npm install my-package ```
  </TabItem>
  <TabItem value="yarn" label="yarn">
    ```bash yarn add my-package ```
  </TabItem>
</Tabs>
````

**Key gotcha:** MDX v3 is stricter than Markdown — unclosed HTML tags, bare `{` characters, and `<` comparisons in text will cause build errors. Escape them with `\{` and `\<` or use code fences.

> **Full example:** See [examples/content.md](examples/content.md) for code block features, asset handling, and blog plugin configuration.

---

### Pattern 5: Versioning

Docusaurus versions docs by snapshotting the entire `docs/` directory. The current working copy is always `current` (next/unreleased). Cut a release version when shipping.

```bash
# Snapshot current docs as version 1.0.0
npx docusaurus docs:version 1.0.0
```

This creates `versioned_docs/version-1.0.0/` and `versioned_sidebars/version-1.0.0-sidebars.json`. Configure version behavior in the docs plugin:

```javascript
// In preset-classic docs options
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'],
}
```

**Key gotcha:** Versioned docs are full copies, not diffs. Each version doubles the build time and output size. Use `onlyIncludeVersions` in development to speed up builds. Only version when you have actual API/feature differences — not for every release.

> **Full example:** See [examples/customization.md](examples/customization.md#versioning) for version banner configuration and multi-version navigation.

</patterns>

---

<decision_framework>

## Decision Framework

### Sidebar Strategy

```
How should the sidebar be organized?
|-- Small docs site (< 30 pages)?
|   +-- Use autogenerated sidebar with sidebar_position front matter
|-- Large docs site with many sections?
|   |-- Sections map cleanly to directories? -> Autogenerated + _category_.json
|   +-- Need cross-directory grouping? -> Manual sidebar in sidebars.js
|-- Multiple independent doc sets (e.g., API + guides)?
|   +-- Use multiple docs plugin instances, each with its own sidebar
+-- Need to mix auto and manual?
    +-- Use autogenerated for most, with manual items for special entries
```

### Swizzling Approach

```
Need to customize a theme component?
|-- Adding content around the component? -> Wrap (--wrap)
|-- Need to change the component's internal logic? -> Check safety level first
|   |-- Component marked "Safe"? -> Eject is acceptable
|   |-- Component marked "Unsafe"? -> Wrap if possible, eject only as last resort
|   +-- Component marked "Forbidden"? -> Do not swizzle — find another approach
+-- Just changing colors/spacing? -> Use CSS variables in custom.css (no swizzle needed)
```

### Content Format

```
What kind of page am I creating?
|-- Documentation article? -> .md or .mdx file in docs/
|-- Blog post? -> .md or .mdx file in blog/
|-- Standalone page with custom layout?
|   |-- Mostly content? -> .mdx in src/pages/
|   +-- Mostly interactive/React? -> .tsx in src/pages/
+-- Need to embed React components in docs? -> Use .mdx with imports
```

</decision_framework>

---

<red_flags>

## RED FLAGS

**High Priority Issues:**

- Using `onBrokenLinks: 'ignore'` or `'log'` in production — broken links should fail the build (`'throw'`)
- Ejecting theme components when wrapping would suffice — ejected components miss upstream bug fixes and feature additions
- Using `@theme/ComponentName` import in a swizzle wrapper instead of `@theme-original/ComponentName` — creates an infinite import loop
- Putting all sidebar config in `sidebars.js` manually when autogenerated would work — creates a maintenance burden that falls out of sync with actual docs

**Medium Priority Issues:**

- Versioning every release instead of only when docs content actually changes — bloats build time and output size
- Not setting `onBrokenMarkdownLinks: 'warn'` at minimum — silent broken links accumulate
- Missing `editUrl` in docs plugin config — blocks "Edit this page" links that drive community contributions
- Not using `showLastUpdateTime: true` — readers cannot tell how current a doc page is

**Common Mistakes:**

- Bare `{` or `<` in MDX content causing build failures — escape with `\{` and `\<` or wrap in code fences
- Forgetting to restart the dev server after changing `docusaurus.config.js` — config changes are not hot-reloaded
- Using `.md` extension with JSX when `format: 'detect'` is configured — in detect mode, only `.mdx` files support JSX (default mode processes both)
- Creating `_category_.json` with wrong field names (`name` instead of `label`, `order` instead of `position`)
- Importing from `@docusaurus/` packages directly in MDX — use `@theme/` or `@site/` aliases instead

**Gotchas & Edge Cases:**

- `baseUrl` must end with `/` — omitting the trailing slash breaks asset resolution
- Static assets in `static/` are served from root, not from `baseUrl` — use `require()` or `useBaseUrl()` for path-safe references
- Blog authors are configured in `blog/authors.yml`, not in `docusaurus.config.js`
- The `slug` front matter field overrides the URL path derived from the filename — useful for keeping clean URLs when renaming files
- `docs-only mode` requires setting `routeBasePath: '/'` in the docs plugin AND `blog: false` in the preset
- Custom pages in `src/pages/` use the file path as the route — `src/pages/support.tsx` becomes `/support`
- Tabs component state is not shared between instances by default — use `groupId` prop to sync tab selection across the page

</red_flags>

---

<critical_reminders>

## CRITICAL REMINDERS

> **All code must follow project conventions in CLAUDE.md**

**(You MUST use `docusaurus.config.js` as the single source of truth for all site configuration)**

**(You MUST use `@docusaurus/preset-classic` unless you have a specific reason to configure plugins individually)**

**(You MUST prefer `--wrap` over `--eject` when swizzling — wrapping preserves upstream updates)**

**(You MUST use front matter `sidebar_position` and `_category_.json` for sidebar ordering — do not fight filesystem-driven conventions)**

**(You MUST NOT mix versioned and unversioned docs in the same plugin instance)**

**Failure to follow these rules will cause broken builds, unmaintainable theme overrides, and sidebar chaos.**

</critical_reminders>
