@mdit/plugin-attrs
Plugins to add attrs to Markdown content.
Usage
import { attrs } from "@mdit/plugin-attrs";
import MarkdownIt from "markdown-it";
const mdIt = new MarkdownIt().use(attrs, {
// your options, optional
});
mdIt.render("# Heading 🎉{#heading}");Syntax
You can use {attrs} to add attrs to Markdown content.
For example, if you want a heading2 "Hello World" with a id "say-hello-world", you can write:
## Hello World {#say-hello-world}If you want a image with class "full-width", you can write:
{.full-width}Also, other attrs are supported, so:
A paragraph with some text. {#p .a .b align=center customize-attr="content with spaces"}will be rendered into:
<p id="p" class="a b" align="center" customize-attr="content with spaces">
A paragraph with some text.
</p>Escaping
Escaping can be done by adding \ to escape the delimiter:
### Heading \{#heading}will be
Heading {#heading}
Security
Because attributes are taken from Markdown content, @mdit/plugin-attrs drops attributes that could execute scripts:
- event handler attributes, i.e. any attribute whose name starts with
on(case-insensitive), such asonclickoronerror; - URL attributes (
href,src,action,formaction,poster,srcset,xlink:href, etc.) whose value uses a dangerous URL scheme (javascript:,vbscript:orfile:), including values obfuscated with leading control characters, tabs or line breaks; data:URL attributes, except the image data URLs accepted by markdown-it (data:image/gif;,data:image/png;,data:image/jpeg;anddata:image/webp;); other data URLs such asdata:text/htmlordata:image/svg+xmlare dropped.
The URL attribute policy matches markdown-it's built-in link validation. This filtering is always applied, even when the attribute is listed in allowed, and parseAttrs behaves the same. Other attributes such as class, id, style, aria-* and data-* are untouched.
The plugin does not sanitize HTML that you explicitly enable through markdown-it's html option. Only enable attrs for trusted Markdown, or restrict generated attributes with allowed.
Options
You can pass options to @mdit/plugin-attrs to customize plugin behavior.
"all" | boolean | MarkdownItAttrRuleName[]"all"Rules to enable.
type MarkdownItAttrRuleName =
| "fence"
| "inline"
| "table"
| "list"
| "hr"
| "heading"
| "softbreak"
| "blockInfo"
| "blockEnd"
// legacy alias of "blockEnd"
| "block"
// opt-in, excluded from "all"
| "tasklist"
| "dl";The default is "all", which enables all rules. This is the most important option, as it controls which Markdown elements will have attrs enabled and affects the performance of the plugin.
If you only need id attrs for headings (for most cases), you shall set rule: ["heading"] to only enable attrs for headings.
The fence rule only applies to fenced code blocks, while the blockInfo rule covers other block tokens carrying attributes on their info line (e.g.: containers from @mdit/plugin-container). The blockEnd rule applies attributes written at the end of a block element - block is its legacy alias.
The tasklist rule supports task list plugins that wrap item contents in a label (e.g. @mdit/plugin-tasklist). Task lists are not part of core markdown-it, so this rule must be enabled explicitly in the rule array and is excluded from "all". The dl rule does the same for definition lists (e.g. @mdit/plugin-dl), whose paragraph-wrapped definitions hide attributes from the other rules.
(string | RegExp)[] | AllowedAttrEntry[][]Allowed attributes.
An empty list means allowing all attributes.
Dangerous attributes (see the Security warning above) are always dropped, even when they are listed here.
interface AllowedAttrEntry {
name: string | RegExp;
value?: (string | RegExp)[];
}You can use the simple format (string | RegExp)[] to only filter by attribute name:
// Only allow class and id attributes
allowed: ["class", "id"];Or use the entry format AllowedAttrEntry[] to constrain allowed values per attribute:
allowed: [
{ name: "referrerpolicy", value: ["no-referrer", "no-referrer-when-downgrade"] },
{ name: /^data-/, value: ["true", "false"] },
{ name: "class" }, // allow class, any value
];booleantruePlace fence attributes on <pre> instead of <code>.
When enabled, attributes on fenced code blocks (e.g. ```js {data-file="index.js"}) are moved from <code> to the outer <pre> tag. This is skipped when a custom fence renderer is already installed.
string'{'Left delimiter for attributes.
string'}'Right delimiter for attributes.
Programmatic Parsing
The package exports a parseAttrs helper so that other tools (e.g.: shiki transformers) can reuse the attrs parsing logic:
import { parseAttrs } from "@mdit/plugin-attrs";
parseAttrs("foo {.bar #baz data-a=b}");
// [["class", "bar"], ["id", "baz"], ["data-a", "b"]]
parseAttrs("foo"); // nullparseAttrs(content, options) returns the parsed attrs as [key, value] tuples, or null when no valid attrs section is found. A valid section yielding no attrs (e.g.: all filtered out by allowed) returns an empty array. Besides left, right and allowed which behave the same as the plugin options, an extra where option is supported:
"start" | "end" | "only""end"Where the attrs section shall be located in the content: at the start, at the end, or the content shall only contain the attrs section.
Demo
All classes are styled with
margin: 4px;padding: 4px;border: 1px solid red;to show the effect.
Text with inline code and , also supporting emphasis and bold.
Text with `inline code`{.inline-code} and {.image}, also supporting _emphasis_{.inline-emphasis} and **bold**{.inline-bold}.block content
block content {.block}const a = 1;```js {.fence}
const a = 1;
```No red border here: the syntax highlighter used by these docs replaces the fence renderer and drops the attributes, as most code highlighting setups do.
| A | B | C | D |
|---|---|---|---|
| A1 | B1 | C1 | D1 |
| A2 | C2 | ||
| C3 | |||
| A | B | C | D |
| ------------------------ | --- | --- | -------------- |
| A1 | B1 | C1 | D1 {rowspan=3} |
| A2 {colspan=2 rowspan=2} | B2 | C2 | D2 |
| A3 | B3 | C3 | D3 |
{.table border=1}- list item
- nested list item
- list item{.list-item}
- nested list item
{.nested}
{.list-wrapper}--- {.horizontal}A line with break
A line with break
{.break}