mrkeyoor.com_
Wed 23 Sept 02:50 UTC
npmUtilsupdated 22 Sept 2026

remark review

remark 15.0.1 is a preconfigured unified processor for Markdown input and Markdown output. It parses CommonMark into mdast, runs plugins that inspect or alter that tree, and serializes mdast back to text. GFM, frontmatter, lint rules, HTML conversion, and table-of-contents generation come from separate plugins. Version 15 moved to unified 11, added an exports entry and typed settings, set fenced code and one-space list indentation as serializer defaults, and requires Node 16; 15.0.1 itself only fixes a typo. Our browser build was 26.1 KB gzipped, so plugin-driven tree processing has a visible client cost.

Verdict

remark 15.0.1 installed in 4.1 seconds and 5 MB in our sandbox, passed npm audit with 0 findings, and produced a 26.1 KB gzipped browser bundle. Install it when Markdown is structured data that plugins must inspect or change; use micromark or a direct renderer for plain HTML output.

We installed it

Lab card: what happened when we installed remarkScreenshot of remark documentation
Install✓ · 4.1s51 packages on disk · 5 MB
ImportESM import works · require() works · ESM package with exports map
Browser26.1 KBgzipped (89.4 KB minified), bundled with esbuild
TypesTypeScript types bundled
Known vulns00 critical · 0 high · 0 moderate · 0 low (npm audit)

Answers from our run

Does remark install cleanly?

Yes. In a fresh container with an empty cache, npm install remark finished in 4 seconds, leaving 51 packages and 5 MB on disk. npm audit reported no known vulnerabilities.

How much does remark add to a browser bundle?

26.1 KB gzipped (89.4 KB minified) when the whole package is bundled for the browser with esbuild. Importing only part of it is usually smaller.

Does remark work with both ESM and CommonJS?

Yes. Both import 'remark' and require('remark') worked in Node 22 in our run. The package is published as ESM with an exports map.

Does remark include TypeScript types?

Yes, type declarations ship inside the package, so no @types install is needed.

remark or micromark: which should you use?

micromark: Use it for standards-focused Markdown parsing or HTML output without an mdast plugin workflow. remark 15.0.1 installed in 4.1 seconds and 5 MB in our sandbox, passed npm audit with 0 findings, and produced a 26.1 KB gzipped browser bundle.

When should you not use remark?

The only job is Markdown-to-HTML rendering; the remark README recommends micromark for that narrower path

API stability4/5The core unified workflow remains parse, use, run, process, and stringify, with remark supplying the Markdown parser and compiler. Version 15 does contain migration work: Node 16 became the floor, an exports entry replaced private paths, dependencies moved to unified 11-era types, fences and list indentation defaults changed, and bulletOrderedOther disappeared. The 15.0.1 patch itself changes only a typo.
Docs5/5The README explains the boundaries among remark, unified, mdast, rehype, micromark, direct mdast utilities, and remark-cli. It includes complete examples for HTML, sanitization, GFM, frontmatter, linting, CLI formatting, custom transforms, typing, compatibility, and hostile-input limits. It also tells readers to use micromark for direct HTML and to review third-party plugins independently, which prevents common over-installation.
Maintenance4/5GitHub reports 8,985 stars, 11 open issues and pull requests, a last push on 2026-07-01, and an unarchived repository. npm 15.0.1 dates to 2023, but the package is a 36 KB wrapper over actively shared unified, parser, serializer, and mdast components rather than a large frozen implementation. The release gap is worth monitoring, while current repository work and the small public surface argue against calling it abandoned.
Ecosystem5/5The npm endpoint counted 5,587,659 downloads during the latest completed week, and the project README points to more than 150 plugins. remark connects CommonMark and mdast to GFM, MDX, lint rules, table-of-contents transforms, rehype HTML processing, vfile messages, CLI workflows, and TypeScript types. Every extra plugin brings its own release and security profile, so ecosystem breadth does not remove dependency review.

Use it if

  • Markdown must be inspected, linted, or rewritten as an mdast tree rather than handled with regular expressions
  • Input and output are both Markdown and a unified plugin pipeline is useful
  • A project needs explicit syntax extensions such as GFM or frontmatter and can install each one separately
  • Custom synchronous or asynchronous transforms need positional syntax-tree data and vfile messages
Skip it if

Setup reality

We installed remark 15.0.1 in a fresh unprivileged Node 22 Bookworm container. npm completed in 4.1 seconds and left 51 packages using 5 MB on disk. The package itself was 36 KB unpacked, declared 4 direct dependencies and no peers, and included TypeScript declarations under MIT. npm audit reported 0 known vulnerabilities.

Version 15 is an ESM package with an exports map. ESM import worked, and the lab's direct require() check also worked under Node 22 despite the ESM package declaration. The documented and portable authoring path is import { remark } from 'remark'. An esbuild browser import measured 89.4 KB minified and 26.1 KB gzipped before application plugins, so keep server-side content work off the client when possible.

The base processor includes remark-parse and remark-stringify. It does not include the remark CLI, GFM, frontmatter, HTML output, sanitization, lint presets, or tree traversal. Each of those is a separate package. HTML output needs a bridge to hast plus an HTML compiler; user-controlled markup needs rehype-sanitize between those stages. Plugin order determines which syntax exists during parsing and which tree later transforms receive.

process() parses, runs transforms, and serializes asynchronously. parse() only creates mdast; stringify() only compiles a tree; use run() or runSync() when you split the stages and still need transforms. Version 15 changed serializer defaults: fenced code is on, listItemIndent defaults to one, and bulletOrderedOther was removed. Formatting can therefore rewrite many lines at once. Test output snapshots before running it across a documentation repository.

Patterns

Parse, transform, and serialize Markdown format-markdown

import { remark } from 'remark';

const file = await remark().process('# Hello, *Mars*!');
console.log(String(file));

process() runs the complete pipeline and returns a promise. It can normalize formatting even when no custom transform is registered.

Build an mdast tree parse-mdast

const processor = remark();
const tree = processor.parse('## Hello *Pluto*!');
console.log(tree.children[0]);

parse() is synchronous and skips transformer plugins. Call processor.run(tree) when registered transforms must also execute.

Write a tree back to Markdown serialize-mdast

const tree = {
  type: 'root',
  children: [{
    type: 'heading',
    depth: 2,
    children: [{ type: 'text', value: 'Status' }],
  }],
};

console.log(remark().stringify(tree));

stringify() expects valid mdast shapes and does not run transforms. Import Root and child node types from mdast in TypeScript code.

Set Markdown output choices configure-format

const file = await remark()
  .use({ bullet: '-', emphasis: '_', fences: true })
  .process('* one\n* two');

Settings reach the parser and compiler. Version 15 already defaults fences to true and listItemIndent to one.

Parse GitHub Flavored Markdown enable-gfm

import remarkGfm from 'remark-gfm';

const file = await remark()
  .use(remarkGfm)
  .process('| A | B |\n| - | - |\n| 1 | 2 |');

remark-gfm is a separate install. The base package parses CommonMark without GFM tables, task lists, autolinks, or strikethrough.

Create frontmatter nodes recognize-frontmatter

import remarkFrontmatter from 'remark-frontmatter';

const processor = remark().use(remarkFrontmatter, ['yaml']);
const tree = processor.parse('---\ntitle: Mars\n---\n\n# Page');

remark-frontmatter recognizes the block and creates a yaml node. It does not turn the node value into a JavaScript object.

Render user Markdown through sanitization safe-html

import remarkRehype from 'remark-rehype';
import rehypeSanitize from 'rehype-sanitize';
import rehypeStringify from 'rehype-stringify';

const file = await remark()
  .use(remarkRehype)
  .use(rehypeSanitize)
  .use(rehypeStringify)
  .process(userMarkdown);

All three plugins are separate packages. Sanitization belongs after mdast becomes hast and before HTML is serialized.

Shift heading depths with a plugin transform-headings

import { visit } from 'unist-util-visit';

function shiftHeadings() {
  return tree => {
    visit(tree, 'heading', node => {
      node.depth = Math.min(6, node.depth + 1);
    });
  };
}

const file = await remark().use(shiftHeadings).process(markdown);

Pass the plugin function to use() without calling it. unist-util-visit is another dependency.

Collect inline link nodes collect-links

import { visit } from 'unist-util-visit';

const tree = remark().parse(markdown);
const urls = [];
visit(tree, 'link', node => urls.push(node.url));

link visits do not include definition or image nodes. Visit those node types separately when the report needs them.

Run lint presets and report messages lint-markdown

import consistent from 'remark-preset-lint-consistent';
import recommended from 'remark-preset-lint-recommended';
import { reporter } from 'vfile-reporter';

const file = await remark().use(consistent).use(recommended).process(markdown);
console.error(reporter(file));

Presets and vfile-reporter are separate installs. Warnings are stored on file.messages and do not automatically reject process().

Await an asynchronous transform async-plugin

function attachTitle() {
  return async (tree, file) => {
    const title = await lookupTitle(file.path);
    tree.children.unshift({
      type: 'heading', depth: 1,
      children: [{ type: 'text', value: title }],
    });
  };
}

await remark().use(attachTitle).process({ path: 'post.md', value: body });

process() and run() support promise-returning transforms. runSync() throws when any plugin performs asynchronous work.

Format Markdown through remark-cli cli-format

npm install --save-dev remark-cli remark-toc
npx remark . --output --use remark-toc

The remark package does not contain the executable. --output rewrites matching files, so inspect the diff or run on a clean worktree.

Alternatives

PackageRegistryPick it when
micromarknpmUse it for standards-focused Markdown parsing or HTML output without an mdast plugin workflow
markdown-itnpmUse it for direct HTML rendering with renderer rules and a broad plugin catalogue
markednpmUse it when a straightforward Markdown parser and renderer is enough
unifiednpmUse it when you need to choose every parser, transformer, and compiler across mixed content formats

More utils guides

lru-cache · ajv · type-fest · p-limit · find-up · js-yaml · the whole shelf →

How this guide is made: grounded in the library's documentation, release notes, changelog, and issue history, on a fixed rubric — not a hands-on install of every release. The 50 most-downloaded entries are additionally install-verified in clean containers. Corrections: contact the desk.