NewYour coding agent can read the release notes before it upgrades.Set up the MCP server →
npm · #4173 most downloaded on npm
A very fast and versatile markdown toolchain. AST, React, React Native, SolidJS, Vue, Markdown, and HTML output available with full customization.
Last release 19 days ago
15 Sep 2026
Ships fairly regularly
a new release about every 3 weeks
Nearly every release is documented
notes for 60 of the last 60 stable releases
1 version withdrawn
withdrawn after publishing
11 years old
193 releases · first in 2015
3fa0c22: Refactored inline formatting parsing to eliminate ReDoS vulnerabilities and improve performance. The previous regex-based approach was suscep…
+--------------------------+------------------------+-----------------------+
| │ simple markdown string │ large markdown string |
+--------------------------+------------------------+-----------------------+
| markdown-to-jsx (next) │ 116,892 ops/sec │ 757 ops/sec |
+--------------------------+------------------------+-----------------------+
| markdown-to-jsx (7.7.16) │ 110,756 ops/sec │ 739 ops/sec |
+--------------------------+------------------------+-----------------------+
450d2bb: Added ast option to compiler to expose the parsed AST directly. When ast: true, the compiler returns the AST structure (ASTNode[]) instead of rendered JSX.
Breaking Changes:
ParserResult has been renamed to ASTNode for clarity. If you were accessing this type directly (e.g., via module augmentation or type manipulation), you'll need to update references from MarkdownToJSX.ParserResult to MarkdownToJSX.ASTNode.First time the AST is accessible to users! This enables:
Usage:
import { compiler } from 'markdown-to-jsx'
import type { MarkdownToJSX } from 'markdown-to-jsx'
// Get the AST structure
const ast: MarkdownToJSX.ASTNode[] = compiler('# Hello world', {
ast: true,
})
// Inspect/modify AST
console.log(ast) // Array of parsed nodes
// Render AST to JSX using createRenderer (not implemented yet)
The AST format is MarkdownToJSX.ASTNode[].
3fa0c22: Refactored inline formatting parsing to eliminate ReDoS vulnerabilities and improve performance. The previous regex-based approach was susceptible to exponential backtracking on certain inputs and had several edge case bugs with nested formatting, escaped characters, and formatting inside links. The new implementation uses a custom iterative scanner that runs in O(n) time and is immune to ReDoS attacks.
This also consolidates multiple formatting rule types into a single unified rule with boolean flags, reducing code duplication and bundle size. Performance has improved measurably on simple markdown strings:
Breaking Changes:
The following RuleType enum values have been removed and consolidated into a single RuleType.textFormatted:
RuleType.textBoldedRuleType.textEmphasizedRuleType.textMarkedRuleType.textStrikethroughedIf you're using these rule types directly (e.g., for custom AST processing or overrides), you'll need to update your code to check for RuleType.textFormatted instead and inspect the node's boolean flags (bold, italic, marked, strikethrough) to determine which formatting is applied.
a421067: fix: overhaul HTML block parsing to eliminate exponential backtracking
Replaced the complex nested regex HTML_BLOCK_ELEMENT_R with an efficient iterative depth-counting algorithm that maintains O(n) complexity. The new implementation uses stateful regex matching with lastIndex to avoid exponential backtracking on nested HTML elements while preserving all existing functionality.
Performance improvements:
e6b1e14: Fix renderer crash on extremely deeply nested markdown content
Previously, rendering markdown with extremely deeply nested content (e.g., thousands of nested bold markers like ****************...text...****************) would cause a stack overflow crash. The renderer now gracefully handles such edge cases by falling back to plain text rendering instead of crashing.
Technical details:
This fix ensures stability even with adversarial or malformed inputs while having no impact on normal markdown documents.
fe95c02: Remove unnecessary wrapper when footnotes are present.
One column per quarter.
acc11ad: Fix null children crashing app in production
acc11ad: Fix null children crashing app in production
When null is passed as children to the <Markdown> component, it would previously crash the app in production. This fix handles this case by converting it to empty string.
Before this fix, the following code would crash in production:
<Markdown>{null}</Markdown>
After this fix, this case is handled gracefully and renders nothing.
7e487bd: Fix the issue where YAML frontmatter in code blocks doesn't render properly.
7e487bd: Fix the issue where YAML frontmatter in code blocks doesn't render properly.
This is done by lowering the parsing priority of Setext headings to match ATX headings; both are now prioritized lower than code blocks.
8e4c270: Mark react as an optional peer dependency as when passing createElement, you don't need React
``` +--------------------------+------------------------+-----------------------+
+--------------------------+------------------------+-----------------------+
| │ simple markdown string │ large markdown string |
+--------------------------+------------------------+-----------------------+
| markdown-to-jsx (next) │ 107,013 ops/sec │ 709 ops/sec |
+--------------------------+------------------------+-----------------------+
| markdown-to-jsx (7.7.13) │ 102,934 ops/sec │ 396 ops/sec |
+--------------------------+------------------------+-----------------------+
da003e4: Fix exponential backtracking issue for unpaired inline delimiter sequences.
4351ef5: Adjust text parsing to not split on double spaces unless followed by a newline.
4a692dc: Fixes the issue where link text containing multiple nested brackets is not parsed correctly.
4a692dc: Fixes the issue where link text containing multiple nested brackets is not parsed correctly.
Before: [title[bracket1][bracket2]](url) fails to parse as a link
After: [title[bracket1][bracket2]](url) correctly parses as a link
bf9dd3d: Unescape content intended for JSX attributes.
``` +--------------------------+------------------------+-----------------------+
+--------------------------+------------------------+-----------------------+
| │ simple markdown string │ large markdown string |
+--------------------------+------------------------+-----------------------+
| markdown-to-jsx (7.7.9) │ 103,280 ops/sec │ 403 ops/sec |
+--------------------------+------------------------+-----------------------+
| markdown-to-jsx (7.7.8) │ 101,922 ops/sec │ 401 ops/sec |
+--------------------------+------------------------+-----------------------+
<pre> blocks.7.7.7 had a performance regression for very long input that has been resolved in 7.7.8, and then some.
7.7.7 had a performance regression for very long input that has been resolved in 7.7.8, and then some.
+--------------------------+------------------------+-----------------------+
| │ simple markdown string │ large markdown string |
+--------------------------+------------------------+-----------------------+
| markdown-to-jsx (7.7.8) │ 104,575 ops/sec │ 386 ops/sec |
+--------------------------+------------------------+-----------------------+
| markdown-to-jsx (7.7.6) │ 89,286 ops/sec │ 329 ops/sec |
+--------------------------+------------------------+-----------------------+
89c87e5: Handle spaces in text as a stop token to improve processing, also adapt paragraph detection to exclude non-atx compliant headings if that opt
89c87e5: Handle spaces in text as a stop token to improve processing, also adapt paragraph detection to exclude non-atx compliant headings if that option is enabled.
Fixes #680
654855b: Sanitize more attributes by default to help address XSS vectors.
0ddaabb: Remove unescaping of content inside fenced code blocks.
``` +--------------------------+------------------------+-----------------------+
+--------------------------+------------------------+-----------------------+
| │ simple markdown string │ large markdown string |
+--------------------------+------------------------+-----------------------+
| markdown-to-jsx (7.7.4) │ 92,671 ops/sec │ 330 ops/sec |
+--------------------------+------------------------+-----------------------+
| markdown-to-jsx (7.7.3) │ 91,164 ops/sec │ 301 ops/sec |
+--------------------------+------------------------+-----------------------+
trimEnd API.8026103: Handle paragraph splitting better, fixes #641.
52a727c: Use ReactNode instead of ReactChild for React 19 compatibility
ReactNode instead of ReactChild for React 19 compatibility9d42449: Factor out unnecessary element cloning.
20777bf: Add support for GFM alert-style blockquotes.
20777bf: Add support for GFM alert-style blockquotes.
> [!Note]
> This is a note-flavored alert blockquote. The "Note" text is injected as a `<header>` by
> default and the blockquote can be styled via the injected class `markdown-alert-note`
> for example.
<Markdown> component to allow for easier composition.React.JSX.* namespace instead of JSX.* for React 19 compatibility.ref attributes when processing inline HTML, React doesn't handle it well.0274445: Fix false detection of tables in some scenarios.
class attribute from arbitrary HTML properly to avoid React warnings.87d8bd3: Handle class attribute from arbitrary HTML properly to avoid React warnings.
class attribute from arbitrary HTML properly to avoid React warnings.2281a4d: Add options.disableAutoLink to customize bare URL handling behavior.
2281a4d: Add options.disableAutoLink to customize bare URL handling behavior.
By default, bare URLs in the markdown document will be converted into an anchor tag. This behavior can be disabled if desired.
<Markdown options={{ disableAutoLink: true }}>
The URL https://quantizor.dev will not be rendered as an anchor tag.
</Markdown>
// or
compiler(
'The URL https://quantizor.dev will not be rendered as an anchor tag.',
{ disableAutoLink: true }
)
// renders:
<span>
The URL https://quantizor.dev will not be rendered as an anchor tag.
</span>
b16f668: Fix issue with lookback cache resulting in false detection of lists inside lists in some scenarios
62a16f3: Allow modifying HTML attribute sanitization when options.sanitizer is passed by the composer.
62a16f3: Allow modifying HTML attribute sanitization when options.sanitizer is passed by the composer.
By default a lightweight URL sanitizer function is provided to avoid common attack vectors that might be placed into the href of an anchor tag, for example. The sanitizer receives the input, the HTML tag being targeted, and the attribute name. The original function is available as a library export called sanitizer.
This can be overridden and replaced with a custom sanitizer if desired via options.sanitizer:
// sanitizer in this situation would receive:
// ('javascript:alert("foo")', 'a', 'href')
<Markdown options={{ sanitizer: (value, tag, attribute) => value }}>
{`[foo](javascript:alert("foo"))`}
</Markdown>
// or
compiler('[foo](javascript:alert("foo"))', {
sanitizer: (value, tag, attribute) => value,
})
7603248: Fix parsing isolation of individual table cells.
a9e5276: Browsers assign element with id to the global scope using the value as the variable name. E.g.: can be referenced via window.analytics. This
a9e5276: Browsers assign element with id to the global scope using the value as the variable name. E.g.: <h1 id="analytics"> can be referenced via window.analytics.
This can be a problem when a name conflict happens. For instance, pages that expect analytics.push() to be a function will stop working if the an element with an id of analytics exists in the page.
In this change, we export the slugify function so that users can easily augment it.
This can be used to avoid variable name conflicts by giving the element a different id.
import { slugify } from 'markdown-to-jsx';
options={{
slugify: str => {
let result = slugify(str)
return result ? '-' + str : result;
}
}}
f5a0079: fix: double newline between consecutive blockquote syntax creates separate blockquotes
f5a0079: fix: double newline between consecutive blockquote syntax creates separate blockquotes
Previously, for consecutive blockquotes they were rendered as one:
Input
> Block A.1
> Block A.2
> Block B.1
Output
<blockquote>
<p>Block A.1</p>
<p>Block A.2</p>
<p>Block.B.1</p>
</blockquote>
This is not compliant with the GFM spec which states that consecutive blocks should be created if there is a blank line between them.
Brackets in link text by @zegl in https://github.com/quantizor/markdown-to-jsx/pull/551
Full Changelog: https://github.com/quantizor/markdown-to-jsx/compare/v7.4.3...v7.4.4
fix: restore x-browser stable sort logic in https://github.com/quantizor/markdown-to-jsx/pull/548 (fixes unclosed HTML tags that showed up for some ed
Full Changelog: https://github.com/quantizor/markdown-to-jsx/compare/v7.4.2...v7.4.3
Re-release 7.4.1 with less existential console screaming
Re-release 7.4.1 with less existential console screaming
Update README.md by @majman in https://github.com/quantizor/markdown-to-jsx/pull/534
Full Changelog: https://github.com/quantizor/markdown-to-jsx/compare/v7.4.0...v7.4.1
markdown-to-jsx v7.4 features a new option `renderRule`! — From the README:
Happy New Year! 🎆
markdown-to-jsx v7.4 features a new option renderRule! — From the README:
Supply your own rendering function that can selectively override how rules are rendered (note, this is different than options.overrides which operates at the HTML tag level and is more general). You can use this functionality to do pretty much anything with an established AST node; here's an example of selectively overriding the "codeBlock" rule to process LaTeX syntax using the @matejmazur/react-katex library:
import { Markdown, RuleType } from 'markdown-to-jsx'
import TeX from '@matejmazur/react-katex'
const exampleContent =
'Some important formula:\n\n```latex\nmathbb{N} = { a in mathbb{Z} : a > 0 }\n```\n'
function App() {
return (
<Markdown
children={exampleContent}
options={{
renderRule(next, node, renderChildren, state) {
if (node.type === RuleType.codeBlock && node.lang === 'latex') {
return (
<TeX as="div" key={state.key}>{String.raw`${node.text}`}</TeX>
)
}
return next()
},
}}
/>
)
}
The README docs around syntax highlighting have also been updated with sample code.
With the new year comes a push toward v8. Performance will be a top priority, reducing the complexity of the library's regexes to increase throughput for SSR use-cases and ideally eliminate rare but frustrating issues like catastrophic backtracking. In addition, the library will be pivoting into more of a pure compiler model, with a React adapter offered and ones added for other major frameworks as well. The idea is anywhere you can run JS, you can use [secret new library name].
Stay tuned and thanks for being part of the journey ✌🏼 Here's to a great 2024 🍾
markdown-to-jsx is maintained by @quantizor, buy him a coffee
Full Changelog: https://github.com/quantizor/markdown-to-jsx/compare/v7.3.2...v7.4.0
fix(types): path to esm types in "exports"
fix(types): path to esm types in "exports"
Full Changelog: https://github.com/probablyup/markdown-to-jsx/compare/v7.3.1...v7.3.2
add dev-time error if trying to provide bad input
Full Changelog: https://github.com/probablyup/markdown-to-jsx/compare/v7.3.0...v7.3.1
chore(deps): bump word-wrap from 1.2.3 to 1.2.4 by @dependabot in https://github.com/probablyup/markdown-to-jsx/pull/505
size-limit tests for index.cjs and index.module.js (replacing index.js) by @nbarrow-inspire-labs in https://github.com/probablyup/markdown-to-jsx/pull/500Full Changelog: https://github.com/probablyup/markdown-to-jsx/compare/v7.2.1...v7.3.0
fix: move types condition to the front by @Andarist in https://github.com/probablyup/markdown-to-jsx/pull/492
types condition to the front by @Andarist in https://github.com/probablyup/markdown-to-jsx/pull/492Full Changelog: https://github.com/probablyup/markdown-to-jsx/compare/v7.2.0...v7.2.1
Resolve issue with catastrophic backtracking regex by @Prestaul in https://github.com/probablyup/markdown-to-jsx/pull/436
==text== by @probablyup in https://github.com/probablyup/markdown-to-jsx/pull/471Full Changelog: https://github.com/probablyup/markdown-to-jsx/compare/v7.1.9...v7.2.0
feat(compiler): allow replacing of special capital html characters by @garywilddev in https://github.com/probablyup/markdown-to-jsx/pull/448
Full Changelog: https://github.com/probablyup/markdown-to-jsx/compare/v7.1.8...v7.1.9
Bump ejs from 3.1.6 to 3.1.8 by @dependabot in https://github.com/probablyup/markdown-to-jsx/pull/452
Full Changelog: https://github.com/probablyup/markdown-to-jsx/compare/v7.1.7...v7.1.8
Disable esModuleInterop and remove unquote dependency by @ecraig12345 in https://github.com/probablyup/markdown-to-jsx/pull/426
Full Changelog: https://github.com/probablyup/markdown-to-jsx/compare/7.1.6...v7.1.7
Allow list items etc. to terminate paragraphs by @ikonst in https://github.com/probablyup/markdown-to-jsx/pull/411
Full Changelog: https://github.com/probablyup/markdown-to-jsx/compare/7.1.5...7.1.6
Fixes a bug with the package.json config for some bundlers
Fixes a bug with the package.json config for some bundlers
Full Changelog: https://github.com/probablyup/markdown-to-jsx/compare/7.1.4...7.1.5
docs: Correcting Preact instructions by @rschristian in https://github.com/probablyup/markdown-to-jsx/pull/402
Full Changelog: https://github.com/probablyup/markdown-to-jsx/compare/7.1.3...7.1.4
fix word misinterpretation for emphasized text (#387) thanks @djskinner
Add null to possible types for wrapper. (#380) thanks @mjfwebb
Add null to possible types for wrapper. (#380) thanks @mjfwebb
Document wrapper and forceWrapper options (#350) @coreyward
wrapper and forceWrapper options (#350) @coreywardAdd support for wrapper, forceWrapper, and renderArray options (#268) thanks @coreyward!
Add support for wrapper, forceWrapper, and renderArray options (#268) thanks @coreyward!
Ship a MarkdownToJSX TS namespace with all relevant types for downstream composition
Ship a MarkdownToJSX TS namespace with all relevant types for downstream composition
Nothing published for this version
Nothing published for this version
6.11.4: Mitigates security vulnerability where maliciously crafted markdown links could use data: or vbscript: urls to trigger an xss injection ( #306…
6.11.4: Mitigates security vulnerability where maliciously crafted markdown links could use data: or vbscript: urls to trigger an xss injection ( #306 / https://www.npmjs.com/advisories/1219 ), even when using options.disableParsingRawHTML
Note that currently, the default options.disableParsingRawHTML = false should still only be used for trusted input, as arbitrary html, including script tags.
6.11.3 has no changes (I held the publish script upside down; the only change from 6.11.2 is the version number 😅)
Nothing published for this version
[FIX] - Footnote references (#304) thanks @csantos1113
[FIX] - Footnote references (#304) thanks @csantos1113
Fix: Support empty style attribute (#296) thanks @cribbles
Fix: Support empty style attribute (#296) thanks @cribbles
Optionally disable HTML parsing
Fix #258: Allow escaping pipes within tables props @ariabuckles
[security] Sanitize href values (#249) by @coreyward
[security] Sanitize href values (#249) by @coreyward
add new option namedCodesToUnicode (#236) (#253) by @JeremiasEh
add new option namedCodesToUnicode (#236) (#253) by @JeremiasEh
[XSS] Ignore case of blacklisted HTML elements (#247) by @jakelazaroff
[XSS] Ignore case of blacklisted HTML elements (#247) by @jakelazaroff
fix a table rendering issue when the first cell is blank #241 thanks @simezi
fix a table rendering issue when the first cell is blank #241 thanks @simezi
Your coding agent can read these notes before it upgrades. Set up the MCP server →