Docusaurus CategoryNav
Add a <CategoryNav /> component to Docusaurus category index pages that auto-generates navigation lists from category-nav.json.
Prerequisites
- •
gray-matternpm dependency - •
generate-category-nav.jsscript (see Step 2) - •
generatenpm script wired intostart/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:
#!/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');
// Customize per project: 'auto' = auto-detect subdirs, [] = flat (no subdirs)
const CATEGORY_STRUCTURE = {
// example: 'auto',
// knowledge: [],
};
The script scans each category directory for .md/.mdx files, extracts titles from frontmatter or H1 headings, reads sidebar_position for sorting, and outputs src/data/category-nav.json.
Key functions needed: findSubdirectories, findMarkdownFiles, extractTitle, extractSidebarPosition, getDocId, isIndexFile, getPagesForDirectory, getCategoryLabel, getCategoryPosition, getIndexInfo.
Output format:
{
"categoryName": {
"pages": [{ "docId": "cat/page", "title": "Page Title", "position": 1 }],
"subcategories": [{
"key": "subdir",
"title": "Subdir Title",
"docId": "cat/subdir/index",
"hasIndex": true,
"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>
}
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 1.
Step 6: Wire up scripts and gitignore
In package.json, add:
"generate:category-nav": "node scripts/generate-category-nav.js"
Ensure generate script calls it, and start/build call generate first.
Add to .gitignore:
src/data/category-nav.json
Install gray-matter if not already present.
Step 7: Verify
Run pnpm run generate and check that src/data/category-nav.json is created. Then run pnpm run build to verify no errors.