mrkeyoor.com_
Tue 22 Sept 18:51 UTC
npmUtilsupdated 22 Sept 2026

turndown review

Turndown 7.2.4 converts an HTML string or DOM node into Markdown by walking elements through ordered replacement rules. Constructor options select heading, bullet, code-fence, emphasis, and link styles. Custom rules can inspect tags and attributes; `keep()` emits chosen elements as HTML, and `remove()` deletes chosen subtrees. Core output follows a CommonMark-oriented set and leaves tables plus strikethrough to a GFM plugin. Version 7.2.4 reverses 7.2.3's line-break normalization inside `<pre>` after that change caused regressions. Conversion is intentionally lossy and does not sanitize hostile markup.

Verdict

Turndown 7.2.4 installed in 1.7 seconds, used 9 MB across 2 packages, and built to 4.1 KB gzipped with 0 audit findings in our sandbox. Use it for owned HTML-to-Markdown imports with explicit rules; avoid it for lossless storage, sanitization, or deep syntax-tree processing.

We installed it

Lab card: what happened when we installed turndownScreenshot of turndown documentation
Install✓ · 1.7s2 packages on disk · 9 MB
ImportESM import works · require() works · CommonJS package
Browser4.1 KBgzipped (10.8 KB minified), bundled with esbuild
Typesno TypeScript types found
Known vulns00 critical · 0 high · 0 moderate · 0 low (npm audit)

Answers from our run

Does turndown install cleanly?

Yes. In a fresh container with an empty cache, npm install turndown finished in 2 seconds, leaving 2 packages and 9 MB on disk. npm audit reported no known vulnerabilities.

How much does turndown add to a browser bundle?

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

Does turndown work with both ESM and CommonJS?

Yes. Both import 'turndown' and require('turndown') worked in Node 22 in our run. The package is published as CommonJS.

Does turndown include TypeScript types?

No type declarations were found in our install, so TypeScript users need their own declarations.

turndown or node-html-markdown: which should you use?

node-html-markdown: Choose it for a TypeScript-first converter aimed at fast Node and batch processing. Turndown 7.2.4 installed in 1.7 seconds, used 9 MB across 2 packages, and built to 4.1 KB gzipped with 0 audit findings in our sandbox.

When should you not use turndown?

Round-trip fidelity matters. Classes, IDs, CSS, layout containers, and most unsupported attributes do not survive as Markdown.

API stability4/5The 7.x API still centers on the service constructor, `turndown()`, output options, `addRule()`, `keep()`, `remove()`, and `use()`. Filters remain strings, arrays, or functions. Exact text output is less stable than those methods: 7.2.3 normalized line breaks in preformatted nodes and 7.2.4 reverted the change on the same day after regressions. Snapshot whitespace, escaping, and code blocks even for patch upgrades.
Docs4/5The README specifies accepted strings and DOM node types, every constructor option and default, browser artifacts, filter forms, replacement arguments, plugin composition, special rules, and the complete precedence order. It also warns that Markdown escaping uses aggressive regular expressions and shows where to override it. Details are thinner around parser differences, hostile-input handling, TypeScript setup, and the 7.2.4 rollback, which requires reading release history.
Maintenance4/5Version 7.2.4 was published on April 3, 2026, and GitHub records a later push on June 23. The unarchived repository has 11,401 stars and 150 open issues and pull requests in the combined counter. Maintainers quickly reverted a faulty preformatted line-break change. Release 7.2.3 also moved the build from Browserify to Rollup and fixed link escaping, while the missing bundled TypeScript types remain unresolved.
Ecosystem4/5npm counted 8,503,518 downloads from August 18 through August 24, 2026. Turndown runs in Node and browsers, accepts live DOM nodes, and has a companion GFM plugin for tables and strikethrough. CommonJS loaded through both require and ESM import in our tests. Its extension graph is much smaller than rehype and remark, so source positions and multi-stage document transforms usually justify a syntax-tree pipeline.

Use it if

  • An editor export or content import needs consistent Markdown from ordinary HTML.
  • Project-specific tags or attributes can be handled with a small replacement function.
  • The same conversion should accept HTML strings in Node and live DOM subtrees in a browser.
  • CommonMark-like text is the target, with a separate plugin acceptable for GitHub tables and strikethrough.
Skip it if

Setup reality

We installed turndown 7.2.4 in a clean Node 22 Bookworm sandbox. npm completed in 1.7 seconds, created 2 packages, and used 9 MB. Turndown declares 1 direct dependency and no peers; the tarball unpacks to 220 KB. npm audit found 0 known vulnerabilities. The package requires Node 18 and npm 9 or newer and uses the MIT license. Our browser build was 10.8 KB minified and 4.1 KB gzipped.

The published entry is CommonJS and has no exports map. Both require and ESM import worked on our Node 22 box. No TypeScript declaration files were present. In Node, the single dependency supplies DOM parsing for string input; browsers use their DOM. Malformed markup can be repaired differently by those parsers, so run fixtures in the same environment that handles production conversions.

Defaults are visible in output: setext headings, * bullets, indented code blocks, _ emphasis, and inline links. Configure the service once to match repository conventions instead of post-processing Markdown with regexes. GitHub tables, task-style input, and strikethrough are outside the core rules. Add turndown-plugin-gfm and call use(gfm) before converting content that depends on them.

Precedence can surprise customizers. Blank handling runs first, followed by added rules, built-ins, keep filters, remove filters, and the fallback. Thus keep('a') cannot override the normal link conversion; an added rule must handle it. Version 7.2.4 also restores the pre-7.2.3 treatment of <br> inside <pre>. Snapshot nested lists, whitespace, links, code fences, broken markup, and custom elements before changing package versions or rule order.

Patterns

Convert a string of HTML convert-html-fragment

const TurndownService = require('turndown');

const service = new TurndownService();
const markdown = service.turndown(
  '<h1>Hello</h1><p>Welcome.</p>',
);

The result keeps textual structure, while layout wrappers and most attributes disappear.

Convert an existing DOM element convert-browser-subtree

import TurndownService from 'turndown';

const article = document.querySelector('article');
if (!article) throw new Error('article missing');
const markdown = new TurndownService().turndown(article);

Element, Document, and DocumentFragment inputs are accepted. Broken HTML may parse differently in a browser and Node.

Match repository conventions set-markdown-style

const service = new TurndownService({
  headingStyle: 'atx',
  bulletListMarker: '-',
  codeBlockStyle: 'fenced',
  fence: '```',
  emDelimiter: '*',
});

Without these options, headings are setext, bullets use `*`, code is indented, and emphasis uses `_`.

Convert GitHub-flavored constructs add-gfm-rules

const TurndownService = require('turndown');
const {gfm} = require('turndown-plugin-gfm');

const service = new TurndownService();
service.use(gfm);
const markdown = service.turndown(html);

turndown-plugin-gfm is a separate package that supplies table, task-list, and strikethrough rules.

Convert highlight tags replace-custom-element

service.addRule('highlight', {
  filter: 'mark',
  replacement(content) {
    return `==${content}==`;
  },
});

An added rule outranks built-ins, keep, remove, and fallback handling, but it does not outrank the blank-node rule.

Turn tagged links into mentions match-element-attribute

service.addRule('mention', {
  filter(node) {
    return node.nodeName === 'A' && node.hasAttribute('data-user');
  },
  replacement(_content, node) {
    return `@${node.getAttribute('data-user')}`;
  },
});

Validate attribute contents if another system gives the produced Markdown special meaning.

Keep subscript and superscript tags retain-inline-html

service.keep(['sub', 'sup']);
const markdown = service.turndown(
  '<p>H<sub>2</sub>O and x<sup>2</sup></p>',
);

keep only applies after built-in rules. Use addRule when a standard conversion already handles the element.

Remove navigation and styles delete-html-subtrees

service.remove(['nav', 'style']);
const markdown = service.turndown(html);

remove deletes the matching node and all of its contents; it is not a replacement for HTML sanitization.

Use collapsed link references emit-reference-links

const service = new TurndownService({
  linkStyle: 'referenced',
  linkReferenceStyle: 'collapsed',
});
const markdown = service.turndown(html);

Definitions appear later in the output. Snapshot repeated links because labels depend on conversion order.

Separate unknown block elements customize-unknown-blocks

const service = new TurndownService({
  defaultReplacement(content, node) {
    const block = /^(DIV|SECTION|ASIDE)$/.test(node.nodeName);
    return block ? `\n\n${content}\n\n` : content;
  },
});

The fallback runs only when no added, built-in, keep, or remove rule matches.

Compose a local plugin package-house-rules

function editorialRules(service) {
  service.remove(['nav', 'style']);
  service.addRule('hard-break', {
    filter: 'br',
    replacement: () => '  \n',
  });
}

service.use(editorialRules);

use accepts one plugin or an array. Ordering matters when two plugins match the same element.

Keep whitespace in code blocks preserve-preformatted-text

const service = new TurndownService({
  codeBlockStyle: 'fenced',
  preformattedCode: true,
});
const markdown = service.turndown(
  '<pre><code>line 1\n  line 2</code></pre>',
);

Version 7.2.4 does not normalize `<br>` inside `<pre>` into text newlines; provide literal newlines or a tested rule.

Alternatives

PackageRegistryPick it when
node-html-markdownnpmChoose it for a TypeScript-first converter aimed at fast Node and batch processing.
html-to-mdnpmChoose it when its smaller direct tag conversion surface already matches the input set.
unifiednpmChoose the unified ecosystem when syntax trees need several inspection and transformation stages before Markdown output.

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.