NewYour coding agent can read the release notes before it upgrades.Set up the MCP server →
npm · #3765 most downloaded on npm
Render JSX to an HTML string, with support for Preact components.
Last release 3 days ago
01 Oct 2026
Release timing varies
gaps range from 3 weeks to 7 months
Nearly every release is documented
notes for 60 of the last 60 stable releases
Nothing withdrawn
no release was ever pulled
11 years old
124 releases · first in 2015
Streaming is considered alpha so we're making this breaking change deliberately as part of a minor.
#472 2dff53d Thanks @jacob-ebey! - Replaces the second context argument in our streaming functions with an options object, the context moves to the third position.
Streaming is considered alpha so we're making this breaking change deliberately as part of a minor.
#464 455daf5 Thanks @JoviDeCroock! - Call options._root before the render-to-string invocation
#475 4e92059 Thanks @JoviDeCroock! - Use Node's Writable type for renderToPipeableStream destinations.
#444 e5ff19f Thanks @dsherret! - fix: use cjs types instead of esm for jsx export to fix default export from esm
#477 4b80672 Thanks @JoviDeCroock! - Preserve client-rendered content when delayed streaming chunks arrive after hydration.
#471 0ce2b84 Thanks @JoviDeCroock! - Keep Preact option hooks isolated for the lifetime of asynchronous renders.
#476 5b1d509 Thanks @JoviDeCroock! - Replace streamed Suspense fallbacks when the resolved content is empty.
One column per quarter.
Minor Changes #450 dbc692f Thanks @JoviDeCroock ! - Adjust the comment-marker for streaming to be the same as renderToStringAsync where we use \$s Pat
dbc692f Thanks @JoviDeCroock! - Adjust the comment-marker for streaming to be the same as renderToStringAsync where we use \$se32a4cd Thanks @JoviDeCroock! - Reject unsafe namespaced attribute names before normalizing SVG/XML attribute casing.Patch Changes #457 ce6ef71 Thanks @lemonmade ! - fix: renderToStringAsync produces commas for suspended components with complex children
ce6ef71 Thanks @lemonmade! - fix: renderToStringAsync produces commas for suspended components with complex childrenPatch Changes #446 b7b288c Thanks @JoviDeCroock ! - Fix issues regarding streaming full HTML documents
b7b288c Thanks @JoviDeCroock! - Fix issues regarding streaming full HTML documentsPatch Changes #439 d9ecbf1 Thanks @nwalters512 ! - Ensure useId() produces unique IDs when using the JSX renderer
d9ecbf1 Thanks @nwalters512! - Ensure useId() produces unique IDs when using the JSX rendererPatch Changes #436 6bce3dd Thanks @JoviDeCroock ! - Ensure that in streaming mode throwing a suspension multiple times from the same component works
6bce3dd Thanks @JoviDeCroock! - Ensure that in streaming mode throwing a suspension multiple times from the same component worksPatch Changes #432 62c87e4 Thanks @nwalters512 ! - Preserve whitespace in <pre> / <textarea> when using pretty: true
62c87e4 Thanks @nwalters512! - Preserve whitespace in <pre>/<textarea> when using pretty: true62c87e4 Thanks @nwalters512! - Preserve whitespace in <pre>/<textarea> when using pretty: truePatch Changes #430 f2455d8 Thanks @reteps ! - Fix JSX renderer to correctly render enumerated properties
Patch Changes #427 f3f7aab Thanks @rschristian ! - Correct semver range to support beta releases of Preact v11
f3f7aab Thanks @rschristian! - Correct semver range to support beta releases of Preact v11Minor Changes #376 81e7da3 Thanks @JoviDeCroock ! - Insert comment markers for suspended trees, only in renderToStringAsync Patch Changes #425 e750ba1
81e7da3 Thanks @JoviDeCroock! - Insert comment markers for suspended trees, only in renderToStringAsync#425 e750ba1 Thanks @rschristian! - Expand semver range to support the upcoming v11 beta release
#413 27f340b Thanks @f0x52! - Fix async rendering of multiple suspended components in a single Suspense boundary
#424 e7d241c Thanks @JoviDeCroock! - Fix support for signals, we need to detect and unwrap the signal
#423 ec0b614 Thanks @JoviDeCroock! - Add support for new component bits
#417 441dea2 Thanks @rschristian! - Only abort/report errors from renderToPipeableStream() if the stream hasn't already been closed
Patch Changes #408 8e8e8ac Thanks @f0x52 ! - Ensure that the _parent is kept around across multiple suspensions and avoid circular references. In doin
#408 8e8e8ac Thanks @f0x52! - Ensure that the _parent is kept around across multiple suspensions and avoid circular references.
In doing so our useId hook should always output unique ids during renderingToString.
#409 2afaf31 Thanks @f0x52! - Ensure the renderToStream types of /stream and /stream-node accept a generic for the props of the passed in VNode
#405 0a698f6 Thanks @f0x52! - renderToPipeableStream: expose errors through onError, no longer emit un-catchable error event on internal stream
### Patch Changes - #403 `ed720ef` Thanks @JoviDeCroock! - Improve performance by pre-allocating arrays - #397 `6db4f95` Thanks @rschristian! - Switch
#403 ed720ef Thanks @JoviDeCroock! - Improve performance by pre-allocating arrays
#397 6db4f95 Thanks @rschristian! - Switch HTML_LOWER_CASE regex to match all from line start for a marginal perf benefit.
### Patch Changes - #393 `62ade27` Thanks @rschristian! - Fix spellcheck={false} not rendering as spellcheck="false"
62ade27 Thanks @rschristian! - Fix spellcheck={false} not rendering as spellcheck="false"### Patch Changes - #391 `d80e4dc` Thanks @marvinhagemeister! - Allow any value for class + className as long as it's stringified to match browser beh
d80e4dc Thanks @marvinhagemeister! - Allow any value for class + className as long as it's stringified to match browser behavior.### Patch Changes - #388 `4621fa3` Thanks @marvinhagemeister! - Fix spellCheck={false} not rendering as spellcheck="false"
4621fa3 Thanks @marvinhagemeister! - Fix spellCheck={false} not rendering as spellcheck="false"### Patch Changes - #386 `220ad45` Thanks @JoviDeCroock! - Add async benchmarks and iterate on perf improvements - #383 `883e02b` Thanks @JoviDeCroock
#386 220ad45 Thanks @JoviDeCroock! - Add async benchmarks and iterate on perf improvements
#383 883e02b Thanks @JoviDeCroock! - General performance optimisations
#385 45b8e8b Thanks @JoviDeCroock! - Improve perf a bit by hoisting the typeof check to reduce calling typeof
### Patch Changes - #381 `481b4f3` Thanks @rschristian! - Support dangerouslySetInnerHTML={undefined} with renderToStringAsync
481b4f3 Thanks @rschristian! - Support dangerouslySetInnerHTML={undefined} with renderToStringAsync### Patch Changes - #378 `054dae0` Thanks @ccouzens! - Fix issue where preactRenderToString returns a promise of a promise
### Patch Changes - #372 `bebe4bf` Thanks @jacob-ebey! - fix: stop client runtime from being corrupted fix: insert ooo chunks in the proper order
bebe4bf Thanks @jacob-ebey! - fix: stop client runtime from being corrupted
fix: insert ooo chunks in the proper order### Patch Changes - #370 `7871a51` Thanks @dbergey! - Fix incorrect type for renderToPipeableStream
### Patch Changes - #362 `3044ac2` Thanks @rschristian! - Correct stream exports - #367 `8c7e08f` Thanks @rschristian! - Add types for /stream and /st
#362 3044ac2 Thanks @rschristian! - Correct stream exports
#367 8c7e08f Thanks @rschristian! - Add types for /stream and /stream-node exports
### Patch Changes - #360 `689e88d` Thanks @JoviDeCroock! - Correctly render null as an __html value as an empty string
689e88d Thanks @JoviDeCroock! - Correctly render null as an __html value as an empty string### Patch Changes - #356 `4430ecf` Thanks @JoviDeCroock! - Add back publish with provenance
4430ecf Thanks @JoviDeCroock! - Add back publish with provenance### Minor Changes - #354 `a004914` Thanks @JoviDeCroock! - Introduce a streaming renderer which can be imported from preact-render-to-string/stream an
a004914 Thanks @JoviDeCroock! - Introduce a streaming renderer which can be imported from preact-render-to-string/stream and preact-render-to-string/stream-node#354 a004914 Thanks @JoviDeCroock! - Ensure popoverTarget and popoverTargetAction are serialized to lower case
#354 a004914 Thanks @immitsu! - Fix for shallow rendering incorrectly transforming Fragments into other nodes
#354 a004914 Thanks @JoviDeCroock! - Ensure cellPadding, cellSpacing, and useMap are serialized to lower case
#354 a004914 Thanks @JoviDeCroock! - streaming rendering with Suspense boundaries as flush trigger
### Patch Changes - #347 `7bc77a3` Thanks @marvinhagemeister! - Fix JSX template being detected as a top level Fragment when Deno's jsx precompile opt
#347 7bc77a3 Thanks @marvinhagemeister! - Fix JSX template being detected as a top level Fragment when Deno's jsx precompile option is used
#348 303b8c0 Thanks @marvinhagemeister! - Perf: Remove unnecessary closure when rendering child nodes
### Patch Changes - #344 `27a8b0e` Thanks @JoviDeCroock! - Ensure commonjs also has the async export - #336 `c46fb59` Thanks @marvinhagemeister! - Fix
#344 27a8b0e Thanks @JoviDeCroock! - Ensure commonjs also has the async export
#336 c46fb59 Thanks @marvinhagemeister! - Fix error thrown after suspending not being rethrown.
#339 4462822 Thanks @marvinhagemeister! - Fix invalid parent pointer empty value when rendering a suspended vnode
## 6.4.0 ### Minor Changes - #333 `6acc97a` Thanks @JoviDeCroock and @dios-david! - Allow prepass like behavior where a Promise will be awaited and th
6acc97a Thanks @JoviDeCroock and @dios-david! - Allow prepass like behavior where a Promise
will be awaited and then continued, this is done with the new renderToStringAsync exportfix: mapped precompile children by @marvinhagemeister in https://github.com/preactjs/preact-render-to-string/pull/324
Full Changelog: https://github.com/preactjs/preact-render-to-string/compare/v6.3.0...v6.3.1
6bf321d Thanks @marvinhagemeister! - Fix mapped children not working with Deno's new precompile JSX transform.feat: add support for precompiled JSX by @marvinhagemeister in https://github.com/preactjs/preact-render-to-string/pull/322
Full Changelog: https://github.com/preactjs/preact-render-to-string/compare/6.2.2...v6.3.0
926827c Thanks @marvinhagemeister! - Add support for precompiled JSX transform, see https://deno.com/blog/v1.38#fastest-jsx-transform. Compared to traditional JSX transforms, the precompiled JSX transform tries to pre-serialize as much of the JSX as possible. That way less objects need to be created and serialized which relieves a lot of GC pressure.
// input
<div class="foo">hello</div>;
// output
const tpl = [`<div class="foo">hello</div>`];
jsxTemplate(tpl);
### Patch Changes - #315 `f1d81be` Thanks @JoviDeCroock! - avoid adding double colon for namespaced attributes
f1d81be Thanks @JoviDeCroock! - avoid adding double colon for namespaced attributes# Patch Changes - #308 `a331699` Thanks @marvinhagemeister! - Fix incorrect casing of HTML attributes and SVG attributes - #310 `017a8bb` Thanks @marv
#308 a331699 Thanks @marvinhagemeister! - Fix incorrect casing of HTML attributes and SVG attributes
#310 017a8bb Thanks @marvinhagemeister! - Fix casing of namespaced attribute names
#311 bccd1d6 Thanks @gpoitch! - Apply attribute name handling in pretty mode
This feature is disabled by default and can be enabled by toggling the errorBoundaries option:
#305 568f139 Thanks @marvinhagemeister! - Add support for error boundaries via componentDidCatch and getDerivedStateFromError
This feature is disabled by default and can be enabled by toggling the errorBoundaries option:
import { options } from 'preact';
// Enable error boundaries
options.errorBoundaries = true;
### Minor Changes - #301 `659b456` Thanks @marvinhagemeister! - Add experimental ability to render HTML comments via . When the UNSTABLE_comment prop
659b456 Thanks @marvinhagemeister! - Add experimental ability to render HTML comments via <Fragment UNSTABLE_comment="my-comment" />. When the UNSTABLE_comment prop is present all children of that Fragment will be ignored and a HTML comment will be rendered instead. This feature is added to allow framework authors to experiment with marking DOM for hydration in the client. Note that it's marked as unstable and might change in the future.### Patch Changes - #298 `6a4b8ed` Thanks @shinyama-k! - Fix to add type file for jsx.js
6a4b8ed Thanks @shinyama-k! - Fix to add type file for jsx.js### Patch Changes - #294 `637b302` Thanks @marvinhagemeister! - Bring back exports from 5.x to make migration easier
637b302 Thanks @marvinhagemeister! - Bring back exports from 5.x to make migration easier### Patch Changes - #292 `8f4692c` Thanks @marvinhagemeister! - Fix error in commonjs entry point
8f4692c Thanks @marvinhagemeister! - Fix error in commonjs entry pointstoring the void_elements in a Set
#241 e8cbf66 Thanks @developit! - Improve performance by another 5-10% using switch and short-circuiting, and move pretty-printing + shallow rendering into preact-render-to-string/jsx.
#282 6376f62 Thanks @JoviDeCroock! - Remove trailing space for void_elements, this could fail some test_assertions as
<img /> will become <img/>, the other VOID_ELEMENTS this will be applied for
can be found here
#286 7a8b590 Thanks @JoviDeCroock! - Remove the castin to VNode for preact/debug, this is fixed in Preact >= 10.13.0
#286 7a8b590 Thanks @JoviDeCroock! - Change style calculation to use a Set rather than Regex
#285 a0546fe Thanks @JoviDeCroock! - Fix CJS export
#288 0b04860 Thanks @glenchao! - Enumerate draggable attribute, so the output isn't draggable but draggable="true"
#283 3defa9d Thanks @JoviDeCroock! - Follow up fixes to #278
#289 07ebc66 Thanks @JoviDeCroock! - Support data attribute
#270 5c6877d Thanks @developit! - improve unmount option hook call performance
#278 8cf7cef Thanks @JoviDeCroock! - Improve performance by
x-link regex.toLowerCase() insteadNothing published for this version
### Patch Changes - #257 `8b944b2` Thanks @marvinhagemeister! - Fix preact/debug incorrectly throwing errors on text children
8b944b2 Thanks @marvinhagemeister! - Fix preact/debug incorrectly throwing errors on text children### Patch Changes - #246 `ad35c4c` Thanks @developit and @marvinhagemeister! - Fix object and function children being rendered as undefined * #248 `aa
ad35c4c Thanks @developit and @marvinhagemeister! - Fix object and function children being rendered as undefinedaa12b3c Thanks @marvinhagemeister! - Fix vnode masks not matching with core due to top level component Fragments### Patch Changes - #242 `bd5e5eb` Thanks @JoviDeCroock! - correctly unmount vnodes * #237 `dec7a7a` Thanks @JoviDeCroock! - add parent and children f
bd5e5eb Thanks @JoviDeCroock! - correctly unmount vnodesdec7a7a Thanks @JoviDeCroock! - add parent and children for useId### Patch Changes - #232 `2d5ca74` Thanks @JoviDeCroock! - Performance enhancements * #238 `7cdf4d6` Thanks @developit! - Fix the order of invocation
2d5ca74 Thanks @JoviDeCroock! - Performance enhancements7cdf4d6 Thanks @developit! - Fix the order of invocation for the "before diff" (__b) and "diffed" options hooks.`98cce12` Thanks @JoviDeCroock! - Add types entry to package.json.
#235 2f6d6e8 Thanks @marvinhagemeister! - Remove duplicate type path in package.json. Only one of types or typings is needed.
98cce12 Thanks @JoviDeCroock! - Add types entry to package.json.
e4fe799 Thanks @developit! - Improve string encoding performance by ~50%d83def7 Thanks @JoviDeCroock! - Split up hot paths and make separate path for opts.pretty for performance### Patch Changes - #224 `645f3cb` Thanks @rschristian! - Ensures defaultChecked is serialized as checked attribute * #225 `31ac323` Thanks @JoviDeCro
645f3cb Thanks @rschristian! - Ensures defaultChecked is serialized as checked attribute31ac323 Thanks @JoviDeCroock! - Optimize perf by using smarter string builder### Minor Changes - #219 `250c15f` Thanks @developit! - Implement hook state settling. Setting hook state during the execution of a function component
250c15f Thanks @developit! - Implement hook state settling. Setting hook state during the execution of a function component (eg: in useMemo) will now re-render the component and use the final result. Previously, these updates were dropped.## 5.1.21 ### Patch Changes - #215 `a8672db` Thanks @AleksandrHovhannisyan! - Don't add selected attribute to elements if they already contain that at
a8672db Thanks @AleksandrHovhannisyan! - Don't add selected attribute to <option> elements if they already contain that attribute### Patch Changes - #209 `298d05e` Thanks @rschristian! - On empty className w/ compat, class attribute will no longer be duplicated - #203 Thanks @de
#209 298d05e Thanks @rschristian! - On empty className w/ compat, class attribute will no longer be duplicated
#203 Thanks @developit! - Fix serialize defaultValue as value attribute
298d05e Thanks @rschristian! - On empty className w/ compat, class attribute will no longer be duplicated6d47c5a Thanks @JoviDeCroock! - Fix serialize defaultValue as value attributeFix hooks not working in jsx renderer (#196, thanks @marvinhagemeister)
Prevent possible memory leak in 5.1.17.
This release contains a few minor performance improvements.
This release contains a few minor performance improvements.
encodeEntities fast path for inputs that don't need anything replaced (#194, thanks @jviide)Fragments and improve encodeEntities performance on Intel CPUs (#192, thanks @developit)Whilst the memoization in #184 does improve runtime scores it has no limit on how much it caches. This could lead to a memory leak for content heavy s
Whilst the memoization in #184 does improve runtime scores it has no limit on how much it caches. This could lead to a memory leak for content heavy sites. Let this be a lesson for myself to always put limits on all the things!
Performance: Memoize entity encoding (#184, thanks @marvinhagemeister)
Revert back to old commonjs behaviour. (#183, thanks @marvinhagemeister)
commonjs behaviour. (#183, thanks @marvinhagemeister)Improve HTML entity escaping performance (#181, thanks @marvinhagemeister)
Fix options.diffed and options._commit never being called (#175, thanks @marvinhagemeister)
options.diffed and options._commit never being called (#175, thanks @marvinhagemeister)dangerouslySetInnerHTML is set (#174, thanks @clomie)Invoke options._diff hook (#172, thanks @sventschui)
options._diff hook (#172, thanks @sventschui)defaultValue attribute (#163, thanks @sventschui)Fix SSR for (#161, thanks @developit)
<textarea value> (#161, thanks @developit)voidElements config option (#156, thanks @sventschui)getDerivedStateFromProps (#155, thanks @sventschui)renderToString as renderToStaticMarkup (#157, thanks @sventschui)aria-* attributes (#158, thanks @sventschui)Remove the babel debug jsx properties to the stringified html (#152, thanks @JoviDeCroock )
convert htmlFor to for (#147, thanks @lakshyaranganath)
htmlFor to for (#147, thanks @lakshyaranganath)Add support for node exports (#134, thanks @marvinhagemeister)
exports (#134, thanks @marvinhagemeister)Your coding agent can read these notes before it upgrades. Set up the MCP server →