# MD073 - Table of Contents Validation
## Overview
**Rule ID:** MD073
**Alias:** toc-validation
**Category:** Other
**Fixable:** Yes (marker-based TOCs only)
This rule validates that Table of Contents (TOC) sections match the actual document headings. It detects missing entries, stale entries, and text mismatches.
## Configuration
```toml
[MD073]
# Enable the rule (opt-in, disabled by default)
enabled = true
# Minimum heading level to include (default: 2)
min-level = 2
# Maximum heading level to include (default: 4)
max-level = 4
# Whether TOC order must match document order (default: true)
enforce-order = true
# Spaces per indentation level (default: from MD007, or 2)
# indent = 4
```
### Configuration Options
| `enabled` | boolean | `false` | Whether to enable this rule (opt-in) |
| `min-level` | integer | `2` | Minimum heading level to include in TOC |
| `max-level` | integer | `4` | Maximum heading level to include in TOC |
| `enforce-order` | boolean | `true` | Whether TOC entry order must match heading order |
| `indent` | integer | MD007's indent, or `2` | Spaces per indentation level for nested TOC entries |
### Indentation Configuration
By default, MD073 reads the `indent` value from [MD007](md007.md) to ensure TOC indentation matches your list indentation style. This means if you configure:
```toml
[MD007]
indent = 4
```
Then MD073 will automatically use 4-space indentation for nested TOC entries:
```markdown
- [Installation](#installation)
- [npm](#npm)
- [yarn](#yarn)
- [Usage](#usage)
```
You can override this by setting `indent` explicitly in MD073:
```toml
[MD073]
enabled = true
indent = 2 # Use 2-space indent regardless of MD007 config
```
## TOC Detection Methods
### Marker-Based Detection (Recommended)
Use HTML comments to mark the TOC region explicitly:
```markdown
- [Installation](#installation)
- [Usage](#usage)
```
Alternative stop marker: `<!-- /toc -->`
Marker detection is case-insensitive and allows whitespace variations:
- `<!-- toc -->`
- `<!--toc-->`
- `<!-- TOC -->`
**Auto-fix only works for marker-based TOCs** because markers provide clear, unambiguous boundaries.
### Heading-Based Detection
Detects TOC by looking for headings like "Table of Contents", "Contents", or "TOC":
```markdown
## Table of Contents
- [Installation](#installation)
- [Usage](#usage)
## Installation
...
```
The TOC region ends at the next heading or after two consecutive blank lines.
**Note:** Auto-fix is disabled for heading-based TOCs because the boundaries are fuzzy and could lead to unintended changes.
## What This Rule Checks
### Missing Entries
Headings that exist in the document but don't have corresponding TOC entries:
```markdown
- [Installation](#installation)
## Installation
Content.
## Usage <-- Missing from TOC!
More content.
```
### Stale Entries
TOC entries for headings that no longer exist:
```markdown
- [Installation](#installation)
- [Deleted Section](#deleted-section) <-- Heading doesn't exist!
## Installation
Content.
```
### Text Mismatches
TOC entry text differs from the actual heading:
```markdown
- [Install](#installation) <-- Should be "Installation"
## Installation
Content.
```
### Order Mismatches (when enforce-order = true)
TOC entries in wrong order compared to document headings:
```markdown
- [Usage](#usage) <-- Should come after Installation
- [Installation](#installation)
## Installation
...
## Usage
...
```
## Auto-Fix Behavior
When auto-fix is enabled, this rule:
1. **Only fixes marker-based TOCs** - heading-based TOCs are not auto-fixed
2. Generates a new TOC from all headings after the TOC region
3. Respects `min-level` and `max-level` filters
4. Uses GitHub-style anchors for link generation
5. Uses nested indentation based on heading level
6. Preserves markers (`<!-- toc -->...<!-- tocstop -->`)
The fix is idempotent - running it multiple times produces the same output.
## Examples
### Correct TOC with Markers
```markdown
# My Project
- [Installation](#installation)
- [npm](#npm)
- [yarn](#yarn)
- [Usage](#usage)
## Installation
### npm
npm install my-project
### yarn
yarn add my-project
## Usage
import MyProject from 'my-project';
```
### Correct TOC with Heading
```markdown
# My Project
## Contents
- [Installation](#installation)
- [Usage](#usage)
## Installation
...
## Usage
...
```
## Rationale
Table of Contents sections are valuable for navigation in documentation, but they easily drift out of sync with actual headings when documents are refactored. This rule:
- Catches missing TOC entries when new sections are added
- Identifies stale entries when sections are removed or renamed
- Ensures TOC accurately reflects document structure
- Provides auto-fix to regenerate correct TOC
## Special Cases
### Headings in Code Blocks
Headings inside fenced code blocks are ignored:
```markdown
## Real Heading <-- Included in TOC
\`\`\`markdown
## Example Heading <-- Ignored (in code block)
\`\`\`
```
### Headings with Code Spans
Headings containing code spans work correctly:
```markdown
#### \`check [PATHS...]\`
```
Generates anchor: `#check-paths`
### Duplicate Headings
Duplicate headings get numbered anchors:
```markdown
## FAQ <-- anchor: #faq
## FAQ <-- anchor: #faq-1
## FAQ <-- anchor: #faq-2
```
### Custom Anchors
Custom IDs using `{#custom-id}` syntax are respected:
```markdown
## My Section {#my-custom-anchor}
```
Generates anchor: `#my-custom-anchor` instead of `#my-section`
## See Also
- [MD051](md051.md) - Link fragments should be valid
- [MD041](md041.md) - First line in file should be a top-level heading