19 specialized ESLint rules for common Markdown issues including heading structure, link validation, content requirements, and GitHub Flavored Markdown table validation.
The rules are organized into several categories based on their functionality:
Rules that are enabled by default in the recommended configuration:
interface RecommendedRules {
"markdown/fenced-code-language": "error";
"markdown/heading-increment": "error";
"markdown/no-duplicate-definitions": "error";
"markdown/no-empty-definitions": "error";
"markdown/no-empty-images": "error";
"markdown/no-empty-links": "error";
"markdown/no-invalid-label-refs": "error";
"markdown/no-missing-atx-heading-space": "error";
"markdown/no-missing-label-refs": "error";
"markdown/no-missing-link-fragments": "error";
"markdown/no-multiple-h1": "error";
"markdown/no-reversed-media-syntax": "error";
"markdown/no-space-in-emphasis": "error";
"markdown/no-unused-definitions": "error";
"markdown/require-alt-text": "error";
"markdown/table-column-count": "error";
}Requires language specification for fenced code blocks to ensure proper syntax highlighting.
/**
* Require languages for fenced code blocks
* Recommended: Yes
*/
"markdown/fenced-code-language": "error" | "warn" | "off"Examples:
<!-- ✓ Good -->
```javascript
console.log("Hello");console.log("Hello");#### heading-increment
Enforces heading levels increment by one (no skipping levels like H1 → H3).
```javascript { .api }
/**
* Enforce heading levels increment by one
* Recommended: Yes
*/
"markdown/heading-increment": "error" | "warn" | "off"Examples:
<!-- ✓ Good -->
# Main Title (H1)
## Section (H2)
### Subsection (H3)
<!-- ✗ Bad -->
# Main Title (H1)
### Subsection (H3) - skipped H2Disallows multiple H1 headings in the same document.
/**
* Disallow multiple H1 headings in the same document
* Recommended: Yes
*/
"markdown/no-multiple-h1": "error" | "warn" | "off"Examples:
<!-- ✓ Good -->
# Main Title
## Section 1
## Section 2
<!-- ✗ Bad -->
# Main Title
# Another Main TitleDisallows duplicate link/image reference definitions.
/**
* Disallow duplicate definitions
* Recommended: Yes
*/
"markdown/no-duplicate-definitions": "error" | "warn" | "off"Examples:
<!-- ✓ Good -->
[link]: https://example.com
[another]: https://other.com
<!-- ✗ Bad -->
[link]: https://example.com
[link]: https://duplicate.comDisallows empty link/image reference definitions.
/**
* Disallow empty definitions
* Recommended: Yes
*/
"markdown/no-empty-definitions": "error" | "warn" | "off"Disallows invalid label references that don't match defined labels.
/**
* Disallow invalid label references
* Recommended: Yes
*/
"markdown/no-invalid-label-refs": "error" | "warn" | "off"Disallows missing label references (references without definitions).
/**
* Disallow missing label references
* Recommended: Yes
*/
"markdown/no-missing-label-refs": "error" | "warn" | "off"Disallows link fragments that don't reference valid headings in the document.
/**
* Disallow link fragments that do not reference valid headings
* Recommended: Yes
*/
"markdown/no-missing-link-fragments": "error" | "warn" | "off"Examples:
<!-- ✓ Good -->
# Section One
[Link to section](#section-one)
<!-- ✗ Bad -->
# Section One
[Link to non-existent](#section-two)Disallows unused link/image reference definitions.
/**
* Disallow unused definitions
* Recommended: Yes
*/
"markdown/no-unused-definitions": "error" | "warn" | "off"Disallows empty images (images without src/url).
/**
* Disallow empty images
* Recommended: Yes
*/
"markdown/no-empty-images": "error" | "warn" | "off"Disallows empty links (links without href/url).
/**
* Disallow empty links
* Recommended: Yes
*/
"markdown/no-empty-links": "error" | "warn" | "off"Requires alternative text for images for accessibility.
/**
* Require alternative text for images
* Recommended: Yes
*/
"markdown/require-alt-text": "error" | "warn" | "off"Examples:
<!-- ✓ Good -->

<!-- ✗ Bad -->
Disallows ATX headings without space after hash characters.
/**
* Disallow headings without a space after the hash characters
* Recommended: Yes
*/
"markdown/no-missing-atx-heading-space": "error" | "warn" | "off"Examples:
<!-- ✓ Good -->
# Heading with space
<!-- ✗ Bad -->
#Heading without spaceDisallows reversed link and image syntax (common mistake).
/**
* Disallow reversed link and image syntax
* Recommended: Yes
*/
"markdown/no-reversed-media-syntax": "error" | "warn" | "off"Examples:
<!-- ✓ Good -->

[link text](url)
<!-- ✗ Bad -->
(image.png)[alt text]
(url)[link text]Disallows spaces around emphasis markers (italics and bold).
/**
* Disallow spaces around emphasis markers
* Recommended: Yes
*/
"markdown/no-space-in-emphasis": "error" | "warn" | "off"Examples:
<!-- ✓ Good -->
*italic text*
**bold text**
<!-- ✗ Bad -->
* italic text *
** bold text **Disallows data rows in GFM tables from having more cells than the header row.
/**
* Disallow data rows in a GitHub Flavored Markdown table from having more cells than the header row
* Recommended: Yes
*/
"markdown/table-column-count": "error" | "warn" | "off"Examples:
<!-- ✓ Good -->
| Name | Age |
|------|-----|
| John | 25 |
<!-- ✗ Bad -->
| Name | Age |
|------|-----|
| John | 25 | Extra |Rules that are not enabled by default but can be configured manually:
Disallows bare URLs (URLs without link syntax).
/**
* Disallow bare URLs
* Recommended: No
*/
"markdown/no-bare-urls": "error" | "warn" | "off"Examples:
<!-- ✓ Good -->
[Example](https://example.com)
<!-- ✗ Bad (when rule is enabled) -->
https://example.comDisallows duplicate headings in the same document.
/**
* Disallow duplicate headings in the same document
* Recommended: No
*/
"markdown/no-duplicate-headings": "error" | "warn" | "off"Disallows HTML tags in Markdown content.
/**
* Disallow HTML tags
* Recommended: No
* Options: { allowed?: string[], allowedIgnoreCase?: boolean }
*/
"markdown/no-html": "error" | "warn" | "off" | ["error", {
allowed?: string[];
allowedIgnoreCase?: boolean;
}]Configuration Examples:
// Disallow all HTML
"markdown/no-html": "error"
// Allow specific HTML tags
"markdown/no-html": ["error", {
allowed: ["br", "strong", "em"]
}]
// Allow specific tags (case insensitive)
"markdown/no-html": ["error", {
allowed: ["BR", "STRONG"],
allowedIgnoreCase: true
}]/**
* Base rule definition for Markdown rules
*/
interface MarkdownRuleDefinition<Options = {}> {
meta: {
type: "problem" | "suggestion" | "layout";
docs: {
description: string;
recommended: boolean;
url?: string;
};
fixable?: "code" | "whitespace";
schema?: JSONSchema7 | JSONSchema7[];
messages: { [messageId: string]: string };
};
create(context: RuleContext): MarkdownRuleVisitor;
}
/**
* Rule context with Markdown-specific methods
*/
interface RuleContext {
report(descriptor: ReportDescriptor): void;
getSourceCode(): MarkdownSourceCode;
// ... other ESLint context methods
}
/**
* Rule visitor for Markdown AST nodes
*/
interface MarkdownRuleVisitor {
root?(node: Root): void;
heading?(node: Heading): void;
paragraph?(node: Paragraph): void;
link?(node: Link): void;
image?(node: Image): void;
code?(node: Code): void;
html?(node: Html): void;
table?(node: Table): void;
// ... exit handlers with :exit suffix
}/**
* Options for no-html rule
*/
interface NoHtmlOptions {
allowed?: string[];
allowedIgnoreCase?: boolean;
}
/**
* Generic rule configuration
*/
type RuleConfig<T = any> =
| "off"
| "warn"
| "error"
| ["off"]
| ["warn"]
| ["error"]
| ["warn", T]
| ["error", T];import markdown from "@eslint/markdown";
export default [
{
files: ["**/*.md"],
plugins: { markdown },
language: "markdown/gfm",
rules: {
// Enable specific rules
"markdown/no-html": "error",
"markdown/no-bare-urls": "warn",
// Configure rule with options
"markdown/no-html": ["error", {
allowed: ["br", "strong", "em"],
allowedIgnoreCase: true
}],
// Disable specific rules
"markdown/no-duplicate-headings": "off"
}
}
];export default [
// Default rules for all Markdown files
{
files: ["**/*.md"],
plugins: { markdown },
language: "markdown/commonmark",
rules: {
"markdown/no-html": "error"
}
},
// Allow HTML in README files
{
files: ["README.md", "**/README.md"],
rules: {
"markdown/no-html": "off"
}
}
];