Add DocsSitemap component with expand/collapse navigation to a Docusaurus site's landing page. Generates doc-titles.json from markdown frontmatter and renders a full docs sitemap with collapsible sections. Use when the user says 'doc titles', 'docs sitemap', 'docusaurus doc title', or wants a full documentation index on their Docusaurus site.
Install
npx skillscat add takazudo/claude-resources/dev-docusaurus-doc-title Install via the SkillsCat registry.
Docusaurus DocsSitemap (Doc Titles)
Add a DocsSitemap component that reads sidebars.ts (or .js) and doc-titles.json to render an expand/collapse full-page navigation on the Docusaurus landing page.
Prerequisites
gray-matternpm dependency- Docusaurus site with
sidebars.ts(or.js) routeBasePathset to/docs(sosrc/pages/index.tsxcan serve as landing page)
Step 1: Detect Docusaurus Root
Find the directory containing docusaurus.config.ts (or docusaurus.config.js). This is the Docusaurus root directory, referred to as {DOCUSAURUS_ROOT} throughout this skill. All file paths below are relative to this root.
Step 2: Create the doc-titles generation script
Create {DOCUSAURUS_ROOT}/scripts/generate-doc-titles.js:
#!/usr/bin/env node
const fs = require('fs');
const path = require('path');
const matter = require('gray-matter');
const DOCS_DIR = path.join(__dirname, '../docs');
const OUTPUT_FILE = path.join(__dirname, '../src/data/doc-titles.json');The script recursively finds all .md/.mdx files in docs/, extracts title from frontmatter or first H1 heading, and outputs src/data/doc-titles.json as { "docId": "Title" }.
Step 3: Create the DocsSitemap component
Create {DOCUSAURUS_ROOT}/src/components/DocsSitemap/index.tsx:
import { Fragment, type ReactNode } from 'react';
import useBaseUrl from '@docusaurus/useBaseUrl';
import sidebars from '@site/sidebars';
import docTitles from '@site/src/data/doc-titles.json';
function generateLabel(sidebarId: string): string {
const name = sidebarId.replace(/Sidebar$/, '');
const specialCases: Record<string, string> = {
inbox: 'INBOX',
subp: 'SUB-P',
data: 'DATA',
api: 'API',
};
if (specialCases[name.toLowerCase()]) return specialCases[name.toLowerCase()];
return name.charAt(0).toUpperCase() + name.slice(1);
}Key features:
- Reads sidebar entries from
sidebars.ts(or.js) - For
autogeneratedsidebar items, resolves docs using structured data with proper subcategory nesting - Renders each sidebar as a
<details>/<summary>section (open by default) - Animated chevron (
▶) rotates 90deg when open - Hover effect on summary uses
--ifm-color-primary - Uses
Fragmentwith keys (not<>) to avoid React key warnings when rendering multiple items from autogenerated data
CI compatibility: generate data before quality checks
The auto-generated JSON files (doc-titles.json, category-nav.json) are gitignored, so they don't exist at checkout. If CI runs typecheck or lint before build, these checks will fail because import statements reference missing files. The fix: run the generate scripts before quality checks in CI.
Example CI action step (before typecheck/lint):
- name: Generate data files
run: |
pnpm run generate-doc-titles
pnpm run generate-category-nav
shell: bash
working-directory: ${{ inputs.working-directory }}Do NOT use require() instead of import — ESLint @typescript-eslint/no-require-imports forbids it.
URL generation: strip /index suffix
Docusaurus routes index docs (e.g., overview/index) to the parent directory URL (/docs/overview), not /docs/overview/index. All link generation must use a helper to strip the /index suffix:
function docIdToUrl(docsBaseUrl: string, docId: string): string {
const path = docId.replace(/\/index$/, '');
return `${docsBaseUrl}${path}`;
}Use docIdToUrl(docsBaseUrl, docId) instead of `${docsBaseUrl}${docId}` for all href attributes.
Resolving autogenerated sidebars
If the project has a category-nav.json (from the /docusaurus-category-nav skill), import it and use it for autogenerated sidebar resolution. This provides structured pages + subcategories with proper nesting:
import categoryNav from '@site/src/data/category-nav.json';For autogenerated items with a dirName, look up categoryNav[dirName] and render its pages and subcategories (with nested page lists). This is much better than the fallback approach.
Fallback (when category-nav.json is not available): filter doc-titles.json entries by dirName/ prefix, excluding dirName/index, and render as flat links.
Step 4: Add to the landing page
Create {DOCUSAURUS_ROOT}/src/pages/index.tsx that imports and renders <DocsSitemap />. This requires routeBasePath in docusaurus.config.ts (or .js) to be /docs (not /).
Minimal landing page:
import Layout from '@theme/Layout';
import DocsSitemap from '@site/src/components/DocsSitemap';
export default function Home() {
return (
<Layout>
<main style={{ padding: '2rem', maxWidth: '900px', margin: '0 auto' }}>
<DocsSitemap />
</main>
</Layout>
);
}Step 5: Update config
In docusaurus.config.ts (or .js):
- Change
routeBasePath: '/'torouteBasePath: '/docs' - Update search plugin's
docsRouteBasePathto/docs
Delete {DOCUSAURUS_ROOT}/docs/intro.md if it has slug: / (it conflicts with the new landing page).
Update any absolute doc links from /category/page to /docs/category/page.
Step 6: Wire up scripts and gitignore
In package.json, add:
"generate:doc-titles": "node scripts/generate-doc-titles.js"Ensure generate script calls it, and start/build call generate first.
Add to .gitignore:
src/data/doc-titles.jsonStep 7: Verify
Run pnpm run generate then pnpm run build. Verify the landing page shows expandable sidebar sections with all doc links.