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 plugins are the primary way to inspect and transform markdown content. Every plugin is a plain JavaScript function that hooks into the unified processor’s lifecycle. When the processor runs, it calls each plugin in order, giving it the opportunity to read or mutate the mdast syntax tree before the final output is produced. Because the tree is just a JavaScript object, you can use standard data-manipulation techniques — or the purpose-built unist-util-visit utility — to work with it.

Plugin structure

A remark plugin is a function that is passed to .use() on the processor. When called, it returns a transformer: another function that receives the syntax tree and the virtual file. The transformer is where your actual logic lives.
function myRemarkPlugin() {
  return function transformer(tree, file) {
    // inspect or mutate `tree` here
  }
}
The outer function (the plugin itself) runs once at configuration time and can close over any options you want to use. The inner transformer function runs once per document that is processed.

A minimal example: increasing heading depth

The following complete example shows a plugin that increments the depth of every heading in the document. It uses unist-util-visit to walk every node in the tree and act on those with type === 'heading'.
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(myRemarkPlugin)
  .use(remarkStringify)
  .process('# Hi, Saturn!')

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

function myRemarkPlugin() {
  return function (tree) {
    visit(tree, function (node) {
      if (node.type === 'heading') {
        node.depth++
      }
    })
  }
}
Install unist-util-visit before running this:
npm install unist-util-visit
Use unist-util-visit when you need to traverse nodes without caring about their parent. Use unist-util-visit-parents when your transformation requires knowledge of the ancestor nodes — for example, to check whether a link appears inside a heading.

Accepting options

Most real-world plugins are configurable. Accept an options object as the first argument to your plugin function, then reference it inside the transformer:
function myPlugin(options = {}) {
  return function (tree, file) {
    // use options.someField, options.anotherField, etc.
  }
}
Callers pass options as the second argument to .use():
unified()
  .use(remarkParse)
  .use(myPlugin, {someField: true})
  .use(remarkStringify)

Async transformers

Transformers may be asynchronous. Return a Promise from the transformer function and unified will wait for it to resolve before continuing to the next plugin:
function myAsyncPlugin() {
  return async function (tree, file) {
    const data = await fetchSomeData()
    // mutate tree using data
  }
}

TypeScript typing

The remark ecosystem is fully typed with TypeScript. Annotating your plugin with JSDoc (or TypeScript directly) lets the type checker validate the tree structure and your plugin’s options at compile time. The following example shows the recommended pattern:
/**
 * @import {Root} from 'mdast'
 * @import {VFile} from 'vfile'
 */

/**
 * @typedef Options
 *   Configuration.
 * @property {boolean | null | undefined} [someField]
 *   Some option (optional).
 */

/**
 * My plugin.
 *
 * @param {Options | null | undefined} [options]
 *   Configuration (optional).
 * @returns
 *   Transform.
 */
export function myRemarkPluginAcceptingOptions(options) {
  /**
   * @param {Root} tree
   * @param {VFile} file
   * @returns {undefined}
   */
  return function (tree, file) {
    // Do things.
  }
}
Types for all mdast node shapes are available in the @types/mdast package:
npm install --save-dev @types/mdast

Registering syntax extensions

Some plugins don’t just transform the tree — they extend the markdown syntax itself by teaching the parser and serializer to recognize new constructs (such as math, directives, or MDX). These plugins work by registering extensions on the processor’s data store:
  • data('micromarkExtensions') — extends the tokenizer so the new syntax is recognized during parsing.
  • data('fromMarkdownExtensions') — teaches mdast-util-from-markdown how to turn the new tokens into mdast nodes.
  • data('toMarkdownExtensions') — teaches mdast-util-to-markdown how to serialize those nodes back to markdown text.
function myRemarkSyntaxPlugin() {
  const data = this.data()

  const micromarkExtensions = data.micromarkExtensions ?? (data.micromarkExtensions = [])
  const fromMarkdownExtensions = data.fromMarkdownExtensions ?? (data.fromMarkdownExtensions = [])
  const toMarkdownExtensions = data.toMarkdownExtensions ?? (data.toMarkdownExtensions = [])

  micromarkExtensions.push(myMicromarkExtension)
  fromMarkdownExtensions.push(myFromMarkdownExtension)
  toMarkdownExtensions.push(myToMarkdownExtension)
}
In practice, syntax plugins like remark-gfm, remark-math, and remark-directive handle this plumbing for you — you rarely need to wire it up manually unless you are authoring a new syntax extension from scratch.

Next steps

Extensions

Explore ready-made syntax extension plugins for GFM, math, and more.

Markdown to HTML

See how to combine remark plugins with the rehype ecosystem.

Build docs developers (and LLMs) love