Takazudo

dev-docusaurus-category-nav

Add auto-generated CategoryNav component to Docusaurus category index pages. Use when adding category navigation to a Docusaurus doc site, or when the user says 'category nav', 'add category navigation', or 'docusaurus category nav'.

Takazudo 10 1 Updated 3mo ago
GitHub

Install

npx skillscat add takazudo/claude-resources/dev-docusaurus-category-nav

Install via the SkillsCat registry.

SKILL.md

Docusaurus CategoryNav

Add a <CategoryNav /> component to Docusaurus category index pages that auto-generates navigation lists from category-nav.json.

Prerequisites

  • gray-matter npm dependency
  • generate-category-nav.js script (see Step 3)
  • generate npm script wired into start/build

Step 1: Detect Docusaurus Root

Find the Docusaurus root directory by locating docusaurus.config.ts or docusaurus.config.js in the project. This directory is referred to as {DOCUSAURUS_ROOT} in all subsequent steps. Do not assume it is doc/ or any other hardcoded path.

Step 2: Ask the user for section title

Ask the user what heading to use above the CategoryNav component. Default: Documents in this category

Step 3: Create the generation script

Create {DOCUSAURUS_ROOT}/scripts/generate-category-nav.js:

CATEGORY_STRUCTURE config

Each subcategory supports two modes:

  • dir: Auto-discover .md/.mdx files from a directory. New files are picked up automatically. index.* and CLAUDE.md are excluded. Sorted by sidebar_position frontmatter, then alphabetically.
  • items: Hardcoded list of doc IDs. Use for flat directory structures where files are grouped by naming convention (not subdirectories), or for small fixed lists.
#!/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/category-nav.json');

const CATEGORY_STRUCTURE = {
  // Auto-discover: new files in reports/proposal/ are automatically included
  reports: {
    subcategories: [
      { key: 'proposals', label: 'Proposals', dir: 'reports/proposal' },
    ],
  },
  // Hardcoded: flat structure where grouping is by naming convention
  legacy: {
    subcategories: [
      {
        key: 'basics',
        label: 'Basics',
        items: ['legacy/intro', 'legacy/overview'],
      },
    ],
  },
};

IMPORTANT: Prefer dir over items whenever possible. Only use items when files are in a flat directory and grouped by naming convention rather than subdirectories. Hardcoded items lists create a maintenance trap — new files must be manually added to both sidebars.ts and generate-category-nav.js.

Key functions needed

  • extractTitle(filePath): Extract title from frontmatter or first H1 heading
  • extractSidebarPosition(filePath): Extract sidebar_position from frontmatter (default: Infinity)
  • getMarkdownPath(docId): Resolve doc ID to .md or .mdx file path
  • getPageInfo(docId, position): Get page info (docId, title, position) for a doc ID
  • discoverDocIds(dirRelPath): Scan directory for .md/.mdx files, excluding index.* and CLAUDE.md, sorted by sidebar_position then alphabetically

Output format

{
  "categoryName": {
    "pages": [{ "docId": "cat/page", "title": "Page Title", "position": 1 }],
    "subcategories": [{
      "key": "subdir",
      "title": "Subdir Title",
      "docId": "cat/subdir/first-page",
      "position": 1,
      "pages": [...]
    }]
  }
}

Step 4: Create the CategoryNav component

Create {DOCUSAURUS_ROOT}/src/components/CategoryNav/index.tsx:

import type { ReactNode } from 'react';
import useBaseUrl from '@docusaurus/useBaseUrl';
import categoryNav from '@site/src/data/category-nav.json';
import styles from './styles.module.css';

type CategoryKey = keyof typeof categoryNav;

export default function CategoryNav({ category }: { category: CategoryKey }): ReactNode {
  const docsBaseUrl = useBaseUrl('/docs/');
  const data = categoryNav[category];
  // Render pages as <ul><li><a>...</a></li></ul>
  // Render subcategories with nested <ul> for their pages
  // If subcategory hasIndex, render as link; otherwise plain <span>
}

CRITICAL: The useBaseUrl argument MUST match the docs routeBasePath in docusaurus.config. Check the config first:

  • If routeBasePath: '/docs' → use useBaseUrl('/docs/')
  • If routeBasePath: '/' → use useBaseUrl('/')

Using the wrong base URL will cause all CategoryNav links to 404.

CI compatibility: generate data before quality checks

The category-nav.json file is gitignored and generated by scripts before build. If CI runs typecheck or lint before build, these checks will fail because import references a missing file. 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

Do NOT use require() instead of import — ESLint @typescript-eslint/no-require-imports forbids it.

Create {DOCUSAURUS_ROOT}/src/components/CategoryNav/styles.module.css:

.navList { margin: 1rem 0; }
.navItem { margin: 0.25rem 0; }
.navItem a { color: var(--ifm-link-color); }
.navItem a:hover { color: var(--ifm-link-hover-color); }
.subcategoryLabel { color: var(--ifm-font-color-base); }
.empty { color: var(--ifm-color-emphasis-600); font-style: italic; }

Step 5: Add to category index pages

For each category index .md or .mdx file, append:

## Documents in this category

import CategoryNav from '@site/src/components/CategoryNav';

<CategoryNav category="CATEGORY_NAME" />

Replace the ## Documents in this category heading with whatever the user chose in Step 2.

Step 6: Wire up scripts and gitignore

In package.json, add:

"generate-category-nav": "node scripts/generate-category-nav.js"

Ensure start/build scripts call generate-category-nav before docusaurus. Example:

"start": "node scripts/generate-category-nav.js && docusaurus start",
"build": "node scripts/generate-category-nav.js && docusaurus build"

Add to .gitignore:

src/data/category-nav.json

Install gray-matter if not already present.

Step 7: Verify

Run the generate script and check that src/data/category-nav.json is created. Then run the build to verify no errors.