Learn-skills.dev web-meta-framework-docusaurus
Docusaurus 3.x documentation framework — site configuration, docs/blog plugins, sidebars, versioning, MDX, swizzling, and deployment
git clone https://github.com/NeverSight/learn-skills.dev
T=$(mktemp -d) && git clone --depth=1 https://github.com/NeverSight/learn-skills.dev "$T" && mkdir -p ~/.claude/skills && cp -r "$T/data/skills-md/agents-inc/skills/web-meta-framework-docusaurus" ~/.claude/skills/neversight-learn-skills-dev-web-meta-framework-docusaurus && rm -rf "$T"
data/skills-md/agents-inc/skills/web-meta-framework-docusaurus/SKILL.mdDocusaurus
Quick Guide: Docusaurus 3.x is a React-powered static site generator for documentation. Configure everything in
(ESM). Usedocusaurus.config.jsfor docs + blog + pages + sitemap in one preset. Sidebars can be fully autogenerated from filesystem structure using@docusaurus/preset-classicand front matter_category_.json. Customize theme components via swizzling (prefersidebar_positionover--wrap). MDX is the default content format — use front matter for metadata, admonitions for callouts, and import React components directly in--ejectfiles. Version docs with.mdx. Deploy thedocusaurus docs:versionoutput to any static host.build/
<critical_requirements>
CRITICAL: Before Using This Skill
All code must follow project conventions in CLAUDE.md
(You MUST use
(or docusaurus.config.js
) as the single source of truth for all site configuration — never scatter config across multiple files).ts
(You MUST use
unless you have a specific reason to configure plugins individually — the preset bundles docs, blog, pages, sitemap, and theme)@docusaurus/preset-classic
(You MUST prefer
over --wrap
when swizzling — wrapping preserves upstream updates, ejecting creates a maintenance burden)--eject
(You MUST use front matter
and sidebar_position
for sidebar ordering in autogenerated sidebars — do not fight the filesystem-driven convention)_category_.json
(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
(site metadata, presets, plugins, theme, navbar, footer)docusaurus.config.js - 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
not_meta.json
, different routing model_category_.json - Starlight — Astro-based, uses
and content collections, different architecture entirelyastro.config.mjs
Examples
- Core Configuration & Sidebars — docusaurus.config.js, preset-classic, sidebars, front matter, custom pages
- MDX & Content — MDX features, admonitions, tabs, code blocks, assets, blog plugin
- Customization & Deployment — Swizzling, CSS variables, versioning, i18n, search, deployment
Other resources:
- Quick Reference — 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:
- Convention over configuration — filesystem structure drives routing and sidebar generation; fight this and you fight the framework
- Preset-first —
bundles the common plugin set; only decompose into individual plugins when you need multiple docs instances or unusual setupspreset-classic - Content as data — front matter is the metadata layer;
,sidebar_position
,slug
,tags
all live in the document, not in external configcustom_edit_url - Swizzle, don't fork — customize theme components via the swizzle CLI; wrapping preserves upstream compatibility, ejecting creates a snapshot you must maintain
- Static output —
produces a static site; there is no server runtime, no SSR in production, no API routesdocusaurus build
<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.
// 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 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.
// sidebars.js — autogenerated (recommended) export default { docs: [{ type: "autogenerated", dirName: "." }], };
Control ordering and labels via front matter and
_category_.json:
--- sidebar_position: 3 sidebar_label: Quick Start ---
// 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 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.
# 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
// 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 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.
--- 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 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.
# 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:
// 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.
</patterns>Full example: See examples/customization.md for version banner configuration and multi-version navigation.
<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
oronBrokenLinks: 'ignore'
in production — broken links should fail the build ('log'
)'throw' - Ejecting theme components when wrapping would suffice — ejected components miss upstream bug fixes and feature additions
- Using
import in a swizzle wrapper instead of@theme/ComponentName
— creates an infinite import loop@theme-original/ComponentName - Putting all sidebar config in
manually when autogenerated would work — creates a maintenance burden that falls out of sync with actual docssidebars.js
Medium Priority Issues:
- Versioning every release instead of only when docs content actually changes — bloats build time and output size
- Not setting
at minimum — silent broken links accumulateonBrokenMarkdownLinks: 'warn' - Missing
in docs plugin config — blocks "Edit this page" links that drive community contributionseditUrl - Not using
— readers cannot tell how current a doc page isshowLastUpdateTime: true
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
— config changes are not hot-reloadeddocusaurus.config.js - Using
extension with JSX when.md
is configured — in detect mode, onlyformat: 'detect'
files support JSX (default mode processes both).mdx - Creating
with wrong field names (_category_.json
instead ofname
,label
instead oforder
)position - Importing from
packages directly in MDX — use@docusaurus/
or@theme/
aliases instead@site/
Gotchas & Edge Cases:
must end withbaseUrl
— omitting the trailing slash breaks asset resolution/- Static assets in
are served from root, not fromstatic/
— usebaseUrl
orrequire()
for path-safe referencesuseBaseUrl() - Blog authors are configured in
, not inblog/authors.ymldocusaurus.config.js - The
front matter field overrides the URL path derived from the filename — useful for keeping clean URLs when renaming filesslug
requires settingdocs-only mode
in the docs plugin ANDrouteBasePath: '/'
in the presetblog: false- Custom pages in
use the file path as the route —src/pages/
becomessrc/pages/support.tsx/support - Tabs component state is not shared between instances by default — use
prop to sync tab selection across the pagegroupId
</red_flags>
<critical_reminders>
CRITICAL REMINDERS
All code must follow project conventions in CLAUDE.md
(You MUST use
as the single source of truth for all site configuration)docusaurus.config.js
(You MUST use
unless you have a specific reason to configure plugins individually)@docusaurus/preset-classic
(You MUST prefer
over --wrap
when swizzling — wrapping preserves upstream updates)--eject
(You MUST use front matter
and sidebar_position
for sidebar ordering — do not fight filesystem-driven conventions)_category_.json
(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>