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-stringify is a unified plugin that teaches a processor how to take an mdast syntax tree and produce a markdown string as output. It is the stringify half of the remark ecosystem. Internally it delegates to mdast-util-to-markdown, which serializes mdast nodes in a way that is CommonMark-compliant and broadly compatible with other markdown parsers. Every aspect of the output style — bullet characters, emphasis markers, heading format, thematic break style, and more — is configurable through the Options object.

Installation

npm install remark-stringify
This package is ESM only and requires Node.js 16 or later.

API

unified().use(remarkStringify[, options])

Adds support for serializing an mdast syntax tree to a markdown string. This sets processor.compiler so that any subsequent call to .process() or .stringify() on the processor will produce markdown as output. Import: import remarkStringify from 'remark-stringify'
Parameters: options (Options, optional) — output configuration.
Returns: undefined. The plugin mutates the processor by assigning its compiler.
TypeScript type: Plugin<[(Readonly<Options> | null | undefined)?], Root, string>

Options

All options are optional. When an option is omitted, the default value listed below is used. Options can be passed either directly to .use(remarkStringify, options) or globally via .data('settings', options) — the latter is necessary when using the remark package because remark-stringify is already applied there.
bullet
'*' | '+' | '-'
default:"'*'"
The marker character used for items in unordered lists.
bulletOther
'*' | '+' | '-'
default:"'-' (when bullet is '*', otherwise '*')"
A secondary bullet marker used in specific edge cases where the primary bullet character would produce ambiguous output. Must differ from bullet.
bulletOrdered
'.' | ')'
default:"'.'"
The marker character appended after the number in ordered list items (e.g., 1. vs 1)).
closeAtx
boolean
default:"false"
When true, ATX headings are closed with the same number of # signs as the opening sequence — e.g., ## Heading ## instead of ## Heading.
emphasis
'*' | '_'
default:"'*'"
The marker character used to wrap emphasis (italic) runs.
fence
'`' | '~'
default:"'`'"
The marker character used to open and close fenced code blocks.
fences
boolean
default:"true"
When true, fenced code blocks are always used. When false, fenced code is only used when a language is defined, the code is empty, or the code starts or ends with blank lines — otherwise indented code blocks are emitted.
handlers
Handlers
A map of custom node-type handlers that override or extend the default serialization for specific mdast node types. See mdast-util-to-markdown for the Handlers type and handler signature.
incrementListMarker
boolean
default:"true"
When true, the numbers of ordered list items increment sequentially (1., 2., 3.). When false, every item uses 1..
join
Array<Join>
An array of functions that determine how adjacent block-level nodes are joined (i.e., how many blank lines appear between them). See mdast-util-to-markdown for the Join type.
listItemIndent
'mixed' | 'one' | 'tab'
default:"'one'"
Controls how list item content is indented. 'one' indents by the bullet width plus one space. 'tab' indents to the next tab stop. 'mixed' uses 'one' for tight list items and 'tab' for loose ones.
quote
string
default:"\"\\\"\""
Marker used to quote titles in link and image definitions. Accepts '"' (double-quote, the default) or "'" (single-quote).
When true, links are always serialized as inline resource links ([text](url)) even when an autolink (<https://example.com>) would have been sufficient.
rule
'*' | '-' | '_'
default:"'*'"
The marker character repeated to form thematic break (--- / *** / ___) lines.
ruleRepetition
number
default:"3"
The number of marker characters in a thematic break. Must be at least 3.
ruleSpaces
boolean
default:"false"
When true, spaces are inserted between marker characters in thematic breaks — e.g., * * * instead of ***.
setext
boolean
default:"false"
When true, non-empty rank-1 and rank-2 headings are serialized using setext style (Heading\n======= and Heading\n-------) instead of ATX style (# Heading).
strong
'*' | '_'
default:"'*'"
The marker character used to wrap strong (bold) runs.
tightDefinitions
boolean
default:"false"
When true, link/image definitions are joined without a blank line between them.
unsafe
Array<Unsafe>
An array of character safety schemas that describe positions where certain characters must be escaped. This is an advanced option; see mdast-util-to-markdown for the Unsafe type.

Example

The following example shows a common pattern: converting HTML to markdown by passing an HTML document through rehype-parse, converting the hast tree to mdast with rehype-remark, and finally serializing to markdown with remark-stringify:
import rehypeParse from 'rehype-parse'
import rehypeRemark from 'rehype-remark'
import remarkStringify from 'remark-stringify'
import {unified} from 'unified'

const value = `
<h1>Uranus</h1>
<p><b>Uranus</b> is the seventh
<a href="/wiki/Planet" title="Planet">planet</a> from the Sun.</p>
`

const file = await unified()
  .use(rehypeParse)
  .use(rehypeRemark)
  .use(remarkStringify)
  .process(value)

console.log(String(file))

Extensions

Plugins that add support for custom mdast node types register their serialization logic by pushing into the toMarkdownExtensions array on the processor’s data store:
processor.data('toMarkdownExtensions', [myToMarkdownExtension])
remark-stringify reads this array and passes all entries to mdast-util-to-markdown as extensions. This is how plugins such as remark-gfm add serialization support for tables, footnotes, and other GFM constructs without touching remark-stringify internals.

TypeScript

This package is fully typed with TypeScript. It exports the additional type Options. It also augments the unified Settings interface with all Options fields, enabling type-checked .data('settings', ...) calls when this package (or remark) is imported. To activate the registration in a project that uses unified directly, import remark-stringify somewhere in your type declarations:
/**
 * @import {} from 'remark-stringify'
 */

import {unified} from 'unified'

// @ts-expect-error: `thisDoesNotExist` is not a valid option.
unified().data('settings', {thisDoesNotExist: false})
It also registers the toMarkdownExtensions key in the unified Data interface, giving plugins that push extensions into that array full type safety.

Compatibility

remark-stringify@11 is compatible with Node.js 16 and later. The package is ESM only.

Build docs developers (and LLMs) love