Takazudo

dev-docusaurus-doc-title

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.

Takazudo 10 1 Updated 3mo ago
GitHub

Install

npx skillscat add takazudo/claude-resources/dev-docusaurus-doc-title

Install via the SkillsCat registry.

SKILL.md

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-matter npm dependency
  • Docusaurus site with sidebars.ts (or .js)
  • routeBasePath set to /docs (so src/pages/index.tsx can 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 autogenerated sidebar 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 Fragment with 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: '/' to routeBasePath: '/docs'
  • Update search plugin's docsRouteBasePath to /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.json

Step 7: Verify

Run pnpm run generate then pnpm run build. Verify the landing page shows expandable sidebar sections with all doc links.