Warning
This is an internal project, and is not intended for public use. No support or stability guarantees are provided.
The syncPageIndex function automatically maintains index pages by extracting metadata from documentation pages and updating parent directory indexes. It's designed to work with Next.js file-based routing, keeping navigation indexes in sync as pages are added or modified.
Note
This function is typically called by the
transformMarkdownMetadataplugin during the build process. Most users won't need to call it directly.
When you have documentation pages organized in directories:
app/components/
├── page.mdx ← Index page (auto-updated)
├── button/
│ └── page.mdx ← Button docs
├── checkbox/
│ └── page.mdx ← Checkbox docs
└── dialog/
└── page.mdx ← Dialog docs
The syncPageIndex function:
page.mdx with links and descriptionsimport { syncPageIndex } from '@mui/internal-docs-infra/pipeline/syncPageIndex';
await syncPageIndex({
pagePath: './app/components/button/page.mdx',
metadata: {
slug: 'button',
path: './button/page.mdx',
title: 'Button',
description: 'A clickable button component.',
},
});
This updates ./app/components/page.mdx with an entry for the Button page.
Update multiple pages in a single operation (more efficient with file locking):
await syncPageIndex({
pagePath: './app/components/page.mdx', // The index file itself
metadataList: [
{ slug: 'button', path: './button/page.mdx', title: 'Button', description: '...' },
{ slug: 'checkbox', path: './checkbox/page.mdx', title: 'Checkbox', description: '...' },
{ slug: 'dialog', path: './dialog/page.mdx', title: 'Dialog', description: '...' },
],
});
Update all parent indexes up to a base directory:
await syncPageIndex({
pagePath: './app/components/forms/text-field/page.mdx',
metadata: { ... },
updateParents: true,
baseDir: './app',
});
This updates:
./app/components/forms/page.mdx./app/components/page.mdx./app/page.mdxControl which directories receive index updates:
await syncPageIndex({
pagePath: './app/docs/getting-started/page.mdx',
metadata: { ... },
baseDir: './app',
include: ['docs'], // Only update indexes under 'docs'
exclude: ['docs/internal'], // But skip 'docs/internal'
});
Check if indexes are up-to-date without modifying files:
await syncPageIndex({
pagePath: './app/components/button/page.mdx',
metadata: { ... },
errorIfOutOfDate: true, // Throws if index needs updating
onlyUpdateIndexes: true, // Don't create new indexes
});
The function generates markdown with a specific structure:
# Components
[//]: # 'This file is autogenerated, but the following list can be modified. Automatically sorted alphabetically.'
<PagesIndex>
- [Button](#button) - [Full Docs](./button/page.mdx)
- [Checkbox](#checkbox) - [Full Docs](./checkbox/page.mdx)
- [Dialog](#dialog) - [Full Docs](./dialog/page.mdx)
[//]: # 'This file is autogenerated, DO NOT EDIT AFTER THIS LINE, run: pnpm docs-infra validate'
## Button
A clickable button component.
<details>
<summary>Outline</summary>
- Sections:
- Installation
- Usage
- Props
- Examples
</details>
[Read more](./button/page.mdx)
<!-- More entries... -->
</PagesIndex>
Add status indicators that appear in the index list:
await syncPageIndex({
pagePath: './app/components/new-component/page.mdx',
metadata: {
slug: 'new-component',
path: './new-component/page.mdx',
title: 'New Component',
description: 'A brand new component.',
tags: ['New', 'Beta'], // Displays as [New] [Beta] in the index
},
});
For components with API documentation, include parts or exports to make them searchable:
await syncPageIndex({
pagePath: './app/components/dialog/page.mdx',
metadata: {
slug: 'dialog',
path: './dialog/page.mdx',
title: 'Dialog',
description: 'A modal dialog component.',
// For multi-part components
parts: {
Root: { props: ['open', 'onOpenChange'], dataAttributes: ['data-state'] },
Trigger: { props: ['asChild'] },
Content: { props: ['side', 'align'], cssVariables: ['--dialog-width'] },
},
// Or for single exports
exports: {
Dialog: { props: ['open', 'onOpenChange', 'modal'] },
},
},
});
This metadata is used by useSearch to enable searching for specific props, data attributes, and CSS variables.
For links to external resources, use skipDetailSection:
await syncPageIndex({
pagePath: './app/resources/page.mdx',
metadata: {
slug: 'github',
path: 'https://github.com/mui/base-ui',
title: 'GitHub',
tags: ['External'],
skipDetailSection: true, // Don't generate a detail section
},
});
Parses a markdown index file and extracts page metadata:
import { markdownToMetadata } from '@mui/internal-docs-infra/pipeline/syncPageIndex';
const result = await markdownToMetadata(markdownContent);
// result.title - Index title
// result.pages - Array of PageMetadata
// result.description - Optional description
Merges new page metadata into existing markdown:
import { mergeMetadataMarkdown } from '@mui/internal-docs-infra/pipeline/syncPageIndex';
const updatedMarkdown = await mergeMetadataMarkdown(existingMarkdown, newMetadata, options);
The function uses proper-lockfile to prevent concurrent writes:
metadataList) are more efficient as they require only one lockNext.js route groups (directories in parentheses) are handled specially:
(public), (content), etc. are skipped when finding parent directoriesapp/(public)/(content)/components/button/page.mdx
↓
Updates: app/(public)/(content)/components/page.mdx
Title derived from: 'components' → 'Components'
Updates the parent directory’s index file with metadata from a page.
This function:
| Parameter | Type | Description |
|---|---|---|
| options | { pagePath: string; metadata?: PageMetadata; metadataList?: PageMetadata[]; indexTitle?: string; indexFileName?: string; lockOptions?: lockfile.LockOptions; baseDir?: string; updateParents?: boolean; include?: string[]; exclude?: string[]; onlyUpdateIndexes?: boolean; markerDir?: string | false; errorIfOutOfDate?: boolean; indexWrapperComponent?: string; preserveExistingTitleAndSlug?: boolean } |
Promise<void>type SyncPageIndexOptions = {
pagePath: string;
metadata?: PageMetadata;
metadataList?: PageMetadata[];
indexTitle?: string;
indexFileName?: string;
lockOptions?: lockfile.LockOptions;
baseDir?: string;
updateParents?: boolean;
include?: string[];
exclude?: string[];
onlyUpdateIndexes?: boolean;
markerDir?: string | false;
errorIfOutOfDate?: boolean;
indexWrapperComponent?: string;
preserveExistingTitleAndSlug?: boolean;
};docs-infra validate - CLI command that validates indexesloadServerSitemap - Uses page indexes to build sitemapsloadServerPageIndex - Loads page metadata at runtime