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.

When remark processes a markdown document, it does not manipulate raw strings — it first converts the source into a structured syntax tree, operates on that tree, and then serializes it back into output. The format of that tree is defined by mdast: the markdown abstract syntax tree specification.

What is mdast?

mdast is a specification maintained by the syntax-tree organization. It defines how every markdown construct — headings, paragraphs, lists, links, code blocks, emphasis, and more — is represented as a plain JSON object. Because the spec is language-agnostic, the same node shapes appear whether you are working in Node.js, Deno, or a browser. Every node in an mdast tree has at minimum a type field (a string identifying the node kind). Parent nodes also have a children array holding their descendant nodes. Leaf nodes carry a value string instead. Additional fields depend on the node type — for example, a heading node has a depth property (1–6), and a link node has url and title properties. TypeScript types for the entire mdast specification are available in the @types/mdast package and are used throughout the remark ecosystem.

From markdown source to tree

The best way to understand mdast is to see a concrete example. Take this markdown fragment:
## Hello *Pluto*!
remark-parse produces the following tree (positional info omitted for brevity):
{
  type: 'heading',
  depth: 2,
  children: [
    {type: 'text', value: 'Hello '},
    {type: 'emphasis', children: [{type: 'text', value: 'Pluto'}]},
    {type: 'text', value: '!'}
  ]
}
The ## prefix becomes depth: 2 on the heading node. The *Pluto* emphasis creates a nested emphasis node whose single child is a text node. Plain text on either side of the emphasis becomes its own text nodes. The whole document would be wrapped in a root node with this heading as one of its children.

Key node types

The mdast specification defines node types for all standard markdown constructs:
Node typeDescriptionKey fields
rootThe top-level document nodechildren
headingATX or setext heading (#, ##, …)depth (1–6), children
paragraphA block of inline contentchildren
textLiteral text within an inline contextvalue
emphasisItalic text (*…* or _…_)children
strongBold text (**…** or __…__)children
linkInline or reference linkurl, title, children
imageInline or reference imageurl, title, alt
listOrdered or unordered listordered, start, spread, children
listItemA single item in a listchecked, spread, children
codeFenced or indented code blocklang, meta, value
inlineCodeBacktick code spanvalue
blockquoteBlock-level quotationchildren
thematicBreakA horizontal rule (---, ***, ___)(none beyond type)
htmlRaw HTML fragmentvalue
Plugin authors who add syntax extensions (like remark-gfm or remark-math) introduce additional node types on top of this base set.

Position information

Every node produced by remark-parse carries a position property that records exactly where in the source file the node starts and ends:
{
  type: 'heading',
  depth: 2,
  children: [/* … */],
  position: {
    start: {line: 1, column: 1, offset: 0},
    end:   {line: 1, column: 18, offset: 17}
  }
}
  • line — 1-based line number.
  • column — 1-based column number (character position within the line).
  • offset — 0-based character offset from the start of the file.
Position data is invaluable for lint messages, editor integrations, and source maps. When you construct nodes programmatically (for example, when inserting a generated table of contents), you omit position because the node has no corresponding source location.
Some plugins deliberately strip position data to reduce output size. If your plugin depends on positions for error reporting, run it early in the pipeline before any stripping step.

Traversing and modifying the tree

The most common way to walk an mdast tree is with unist-util-visit, a utility that performs a depth-first traversal and calls your visitor function for every matching node type.
import {visit} from 'unist-util-visit'

// Collect all link URLs in the document
function collectLinks() {
  return function (tree) {
    const urls = []

    visit(tree, 'link', function (node) {
      urls.push(node.url)
    })

    console.log(urls)
  }
}
Pass the visitor as a remark transformer plugin (second argument to .use()), and it will receive the fully parsed tree before stringification:
import {unified} from 'unified'
import remarkParse from 'remark-parse'
import remarkStringify from 'remark-stringify'
import {visit} from 'unist-util-visit'

await unified()
  .use(remarkParse)
  .use(collectLinks)
  .use(remarkStringify)
  .process('Read the [docs](https://unifiedjs.com) and the [spec](https://github.com/syntax-tree/mdast).')
You can also mutate nodes directly inside the visitor. The tree is a plain JavaScript object — reassigning properties or splicing the children array is all it takes:
// Double all heading depths (h1 → h2, h2 → h3, …)
function increaseHeadingDepth() {
  return function (tree) {
    visit(tree, 'heading', function (node) {
      node.depth = Math.min(node.depth + 1, 6)
    })
  }
}
The unist-util-visit package works with any unist-compatible tree, so the same patterns apply whether you are working with mdast (remark), hast (rehype), or nlcst (retext).

TypeScript and mdast types

The remark organization and the unified collective are fully typed with TypeScript. Import node types from @types/mdast to get accurate type-checking and autocomplete in your plugins:
/**
 * @import {Root, Heading, Link} from 'mdast'
 */

/**
 * @param {Root} tree
 */
function myPlugin() {
  return function (tree) {
    visit(tree, 'link', function (node) {
      // node is typed as Link — url, title, and children are all known
      console.log(node.url)
    })
  }
}
Using JSDoc imports (as shown above) or TypeScript import type statements keeps your plugin well-typed without adding a compile step to simple scripts.

Further reading

  • mdast specification — the full node type reference, including all fields and their types.
  • unist — the base specification that mdast extends; defines Node, Parent, Literal, and position.
  • @types/mdast — TypeScript types for every mdast node.
  • unist-util-visit — depth-first tree traversal with typed visitor callbacks.

Build docs developers (and LLMs) love