Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/remarkjs/remark/llms.txt

Use this file to discover all available pages before exploring further.

remark’s power comes from its plugins. Every feature beyond bare CommonMark parsing — GitHub Flavored Markdown tables, a generated table of contents, lint checks, math notation, MDX support — is delivered by a plugin. Understanding how plugins are structured lets you use existing ones effectively and write your own when you need something custom.

What is a plugin?

A plugin is a function passed to .use() on a unified processor. It receives an options object (if you pass one) and returns a transformer function. That transformer is called during the transform phase with the parsed mdast tree and the vfile, before the stringifier runs.
import remarkParse from 'remark-parse'
import remarkStringify from 'remark-stringify'
import {unified} from 'unified'

const processor = unified()
  .use(remarkParse)
  .use(myPlugin)           // attach a plugin
  .use(myPlugin, {opt: 1}) // attach with options
  .use(remarkStringify)
Plugins that need no options can skip the wrapping function and export a transformer directly. Plugins that need to configure the parser or compiler (syntax extensions) hook into those phases instead of returning a transformer. Either way, the .use() call is the same from the outside.

Two kinds of plugins

Syntax extensions

Add new parsing and/or serialization support. These plugins hook into micromark (the underlying parser) to teach remark about new syntax — things like GFM tables, math blocks, or frontmatter. Examples: remark-gfm, remark-math, remark-frontmatter, remark-mdx.

Transformers

Inspect or change the mdast tree after parsing. These are plain functions that receive the tree and the vfile and can read, mutate, or replace any part of the tree. Examples: remark-lint, remark-toc, remark-rehype.
Many plugins combine both aspects — for example, remark-gfm extends the parser to recognize GFM syntax and extends the serializer to emit it correctly.

The plugin lifecycle

1

Parse

remark-parse (or a custom parser) converts the markdown source string into an mdast tree. Syntax-extension plugins registered with .use() before parsing add new token types to micromark so novel constructs are recognized.
2

Transform

Each transformer plugin runs in the order it was registered. Each one receives the same tree (potentially mutated by earlier plugins) and the vfile. A plugin can walk the tree, add or remove nodes, attach messages to the vfile, or even replace the tree root entirely.
3

Stringify

remark-stringify (or rehype-stringify if you bridged to rehype) serializes the final tree into the output string. Serialization-extension plugins registered with .use() add handlers for custom node types so they are emitted correctly.

Writing your first plugin

The example below is taken directly from the remark source. It increases the depth of every heading in a document by one — turning every # h1 into ## h2, every ## h2 into ### h3, and so on.
/**
 * @import {Root} from 'mdast'
 */
import remarkParse from 'remark-parse'
import remarkStringify from 'remark-stringify'
import {unified} from 'unified'
import {visit} from 'unist-util-visit'

const file = await unified()
  .use(remarkParse)
  .use(myRemarkPluginToIncreaseHeadings)
  .use(remarkStringify)
  .process('# Hi, Saturn!')

console.log(String(file)) // => '## Hi, Saturn!'

function myRemarkPluginToIncreaseHeadings() {
  /**
   * @param {Root} tree
   */
  return function (tree) {
    visit(tree, function (node) {
      if (node.type === 'heading') {
        node.depth++
      }
    })
  }
}
Key observations:
  • The outer function myRemarkPluginToIncreaseHeadings is the plugin. It could accept an options argument if needed.
  • It returns an inner function — the transformer — which receives the tree.
  • visit from unist-util-visit walks every node in the tree depth-first.
  • Mutating node.depth is all that is needed; the tree is passed by reference.
If your transformer needs to do async work (for example, fetching data for each link), return a Promise from the transformer function. unified will await it before moving to the next plugin.

Plugins with options

Plugins accept options via a second argument to .use(). The outer function receives those options before returning the transformer:
/**
 * @import {Root} from 'mdast'
 */
import {visit} from 'unist-util-visit'

/**
 * @param {{ maxDepth?: number }} options
 */
function remarkCapHeadings(options) {
  const max = options?.maxDepth ?? 3

  /** @param {Root} tree */
  return function (tree) {
    visit(tree, 'heading', function (node) {
      if (node.depth > max) {
        node.depth = max
      }
    })
  }
}
Use it with:
unified()
  .use(remarkParse)
  .use(remarkCapHeadings, {maxDepth: 2})
  .use(remarkStringify)

Extending the parser and serializer

Syntax-extension plugins don’t just return a transformer — they also register low-level extensions on the unified processor using processor.data(). The three keys used by remark are:
  • micromarkExtensions — extensions passed to micromark, the underlying tokenizer. These teach the parser to recognize new syntax tokens (used by remark-parse).
  • fromMarkdownExtensions — extensions passed to mdast-util-from-markdown. These describe how to build mdast nodes from the new tokens (used by remark-parse).
  • toMarkdownExtensions — extensions passed to mdast-util-to-markdown. These describe how to serialize the new node types back to markdown text (used by remark-stringify).
A plugin like remark-gfm registers on all three keys so that GFM syntax is correctly parsed, represented in the tree, and re-serialized. As a plugin author you normally don’t call data() directly — you use an existing syntax-extension package and follow its documentation. But knowing these keys helps when debugging unexpected parsing behavior or building your own micromark integration. The remark ecosystem has over 150 plugins. Below are some of the most widely used:

remark-gfm

Adds full GitHub Flavored Markdown support: autolink literals, footnotes, strikethrough, tables, and tasklists.

remark-lint

Checks markdown for code style consistency and adherence to best practices. Comes with dozens of configurable rules and preset packages.

remark-toc

Generates a table of contents by scanning headings and inserting a linked list under a designated heading (e.g. ## Contents).

remark-rehype

Converts the mdast tree into a hast (HTML AST) tree, bridging the remark and rehype ecosystems so you can apply HTML-level plugins before serializing.

remark-frontmatter

Adds support for YAML, TOML, and other frontmatter formats. The frontmatter block is parsed as its own node type rather than treated as raw content.

remark-math

Adds syntax for inline math ($…$) and math blocks ($$…$$), producing new node types that rehype plugins can render with KaTeX or MathJax.

remark-mdx

Adds full MDX support: JSX, expressions, and ESM import/export statements. The foundation of the MDX ecosystem.

remark-directive

Introduces a generic directive syntax (:name[content]{attr}) that can be used to build custom extensions without writing a micromark integration from scratch.

Finding more plugins

Three reliable ways to discover remark plugins:
  1. awesome-remark — a curated selection of the most useful and well-maintained projects.
  2. List of plugins — the comprehensive list in the remark repository, annotated with compatibility status.
  3. remark-plugin GitHub topic — every public repo tagged with the remark-plugin topic.
Anyone can publish a remark plugin. Before adding a plugin as a dependency, assess its quality: check the maintenance activity, test coverage, open issues, and whether it is compatible with the current version of remark (micromark-based, v10+). Plugins marked ⚠️ in the official list are known to be broken with recent releases.

Naming conventions

If you publish your own plugin, follow the naming conventions used by the ecosystem:
  • Prefix with remark- for plugins that work with remark().use() — for example, remark-my-feature.
  • Use mdast-util- for utilities that work directly with mdast trees without being unified plugins.
  • Use unist-util- for utilities that work with any unist-compatible tree.
  • Add the remark-plugin topic to your GitHub repository and add a keywords entry in package.json so others can find your work.

Build docs developers (and LLMs) love