NewYour coding agent can read the release notes before it upgrades.Set up the MCP server →
npm · #1799 most downloaded on npm
Fast 3kb React-compatible Virtual DOM library.
Last release 9 days ago
08 Sep 2026
Ships fairly regularly
a new release about every 2 weeks
Nearly every release is documented
notes for 60 of the last 60 stable releases
Nothing withdrawn
no release was ever pulled
11 years old
286 releases · first in 2015
Call options.vnode on rerender (#4209, thanks @andrewiggins)
Missing preact import error when using compat (#4206, thanks @JoviDeCroock)
children optional in Provider's typings (#4205, thanks @shicks)One column per quarter.
Support precompiled JSX transform (#4177, thanks @marvinhagemeister)
diffProps function (#4200, thanks @andrewiggins)Update contentEditable attribute values (#4163, thanks @shoonia)
contentEditable attribute values (#4163, thanks @shoonia)elementTiming attribute/property (#4165, thanks @shoonia)exportparts global attribute (#4164, thanks @shoonia)_nextDom field on VNodes (#4166, thanks @andrewiggins)=== to == in a few places where not needed (#4157, thanks @rschristian)Avoid crashing when there is no _children (#4147, thanks @JoviDeCroock)
Warn for illegal children types in /debug (#4108, thanks @JoviDeCroock)
SetStateAction & PropsWithoutRef types to compat (#4132, thanks @rschristian)HTMLProps to compat (#4117, thanks @rschristian)role attribute when it's null or undefined (#4137, thanks @DAreRodz)Fix double unmount (#4105, thanks @developit)
gotpointercapture and lostpointercapture events (#4096, thanks @rschristian)Support the isElement function in preact/compat (#4041, thanks @cbbfcd)
isElement function in preact/compat (#4041, thanks @cbbfcd)isFragment function in preact/compat (#4042, thanks @cbbfcd)ComponentChild as ReactNode in the preact/compat types (#4077, thanks @rschristian)main (#4069, thanks @marvinhagemeister)Port skew based child diffing (#4010, thanks @JoviDeCroock)
We have noticed a few scenario's where children would get unmounted and remounted eagerly due to some offset bugs in our child-diffing, with skew-based diffing we plan to address those. This is already part of v11 and has been introduced there by @andrewiggins. This has now been backported to Preact X so we can benefit from the consistency improvements here as well.
We used to set refs as part of the children, synchronously this caused several issues when a ref would go down a level, i.e. the following example
const App = () => {
return phase === 1 ? <div ref={x}><p>Foo</p></div> : <div><div ref={x}><p>Foo</p></div>
}
In this example when we go from phase 1 to phase 2 we would mount a new ref but only after we bubble back up diffing clean up the outer ref making the ref reset to null.
Fix TS FunctionComponent w/ exactOptionalPropertyTypes (#4025, thanks @rschristian)
Revert controlled components (#4009, thanks @JoviDeCroock)
ForwardRefExoticComponent and RefAttributes types in compat (#3996, thanks @rschristian)<dialog>'s close & cancel events (#4017, thanks @rschristian)Add microtick delay for restoring the value of controlled inputs (#4006, thanks @JoviDeCroock)
Add default props in cloneElement (#3968, thanks @nnhk23)
indeterminate's SignalLike type (#3971, thanks @rschristian)useImperativeHandle (#3960, thanks @andrewiggins)Optimization for aria-/data- attr detection (#3948, thanks @developit)
Skip over function children when rendering (#3919, thanks @andrewiggins)
Switch back to microticks for scheduling as the default (#3911, thanks @marvinhagemeister , @JoviDeCroock)
<textarea> with value prop (#3891, thanks @andrewiggins)currentComponent (#3908, thanks @JoviDeCroock)10.12.1 from #3889 which could lead to a state not updating after a context update was enqueued (#3906, thanks @JoviDeCroock)indeterminate property (#3837, thanks @rschristian)MemoExoticComponent type in preact/compat (#3898, thanks @rschristian)Fix non-numeric numbers passed to width or height attribute not working correctly (#3888, thanks @JoviDeCroock)
width or height attribute not working correctly (#3888, thanks @JoviDeCroock)createContext update being blocked by shouldComponentUpdate (#3889, thanks @marvinhagemeister)Use microtick outside of events (#3879, thanks @JoviDeCroock)
EventHandler<...> to have a this of type void. (#3867, thanks @MicahZoltu)parent in render functions. (#3863, thanks @MicahZoltu)preact-cli (#3816, thanks @rschristian)Add an explicit default export for compatibility with esbuild (#3783, thanks @Verseth)
default export for compatibility with esbuild (#3783, thanks @Verseth)useId uniqueness with shared parents + DOM nodes in between (#3773, thanks @marvinhagemeister)Object.is in useSyncExternalStore (#3776, thanks @zalishchuk)Fix setState order (#3763, thanks @JoviDeCroock)
setState order (#3763, thanks @JoviDeCroock)useId when Fragments are involved (#3758, thanks @marvinhagemeister)<input> elements (#3764, thanks @mwszekely)Fix webpack error when trying to import compat/package.json (#3755, thanks @akselander)
compat/package.json (#3755, thanks @akselander)"types" field for preact/debug (#3732, thanks @marvinhagemeister)_mask property always has the same name in distributed version (#3721, thanks @JoviDeCroock)Add deprecation notice to render()'s replaceNode argument (#3700, thanks @rschristian)
Today we are announcing a new hook: useId. This hook creates stable unique identifiers that are consistent between server-side rendering (using preact-render-to-string) and client-side hydration. The useId() hook is primarily useful for generating identifiers for attributes like aria-labelledby and <label for="...">.
To enable useId() to generate consistent unique identifiers, please ensure you are using preact-render-to-string version 5.2.4 or newer for server-side rendering.
(#3583, thanks @JoviDeCroock)
_parent, _dom and __hooks after unmount (#3709, thanks @JoviDeCroock)ref property could be omitted from reused VNodes (#3696, thanks @JoviDeCroock)errorInfo to useErrorBoundary callback (#3689, thanks @marvinhagemeister)class | className (#3711, thanks @PodaruDragos)render()'s replaceNode argument (#3700, thanks @rschristian)createElement() and h() calls (#3690, thanks @JoviDeCroock)allow for null return from fc (#3683, thanks @JoviDeCroock)
undo jsx-element removal (#3680, thanks @JoviDeCroock)
allow function component with children (#3676, thanks @JoviDeCroock)
fix hooks calling shouldComponentUpdate without context (#3671, thanks @developit)
fix useSyncExternalStore relying on changed render values (#3655, thanks @JoviDeCroock)
useSyncExternalStore relying on changed render values (#3655, thanks @JoviDeCroock)Fix infinite loop in radix-ui which enqueues multiple state updates in the same tick (#3645, thanks @JoviDeCroock )
radix-ui which enqueues multiple state updates in the same tick (#3645, thanks @JoviDeCroock )useSyncExternalStore not working with function values (#3633, thanks @marvinhagemeister )esbuild (#3630, thanks @marvinhagemeister )Microtick —> setTimeout for debouncing renders (mirror change for setTimeout debounce #3608, thanks @JoviDeCroock)
We changed our debounce of our rendering to setTimeout! Why? We've batched using microtasks for the past few major versions because it benchmarked well. This had a side-effect of flushing batched renders between event handlers, which can cause some strange behavior:
<input
type="checkbox"
onChange={onChange}
checked={true}
onClick={onClick}
/>
An additional benefit of this change is that code causing an infinite rendering loop will no longer result in an unresponsive browser tab. Rendering in a loop is now capped to the browser's maximum timer frequency (~250Hz), which makes it possible to pause and debug the code triggering an accidental loop.
We are adding support for the newly added React 18 hooks (apart from useId) (#3568, thanks @JoviDeCroock)
We are adding support for the newly added React 18 hooks (apart from useId) (#3568, thanks @JoviDeCroock)
Add support for svg property shape-rendering (#3577, thanks @DannyvanderJagt)
undefined in useMemo after skipped render (#3580, thanks @marvinhagemeister)useMemo return value after skipped render (#3579, thanks @marvinhagemeister)_pendingValue to mangle.json (#3575, thanks @marvinhagemeister)Clear pending state from repeated renders (#3567, thanks @JoviDeCroock)
Add export maps to the subpackages (#3565, thanks @JoviDeCroock)
Add server.browser.js (#3544, thanks @JoviDeCroock)
server.browser.js (#3544, thanks @JoviDeCroock)textPath SVG type (#3546, thanks @backmeupplz)improve compat types (#3534, thanks @JoviDeCroock)
debounceRendering hook after exceptions (#3530, thanks @robertknight)Add new react-dom/client entry to compat (#3506, thanks @JoviDeCroock)
Add support for errorInfo (#3452, thanks @marvinhagemeister )
errorInfo (#3452, thanks @marvinhagemeister )xlink:href attribute -> hhref (#3453, thanks @pguilbert)__source and __self arguments (#3459, thanks @marvinhagemeister)useImperativeHandle ref to null when the component get unmounted (#3487, thanks @deadem)defaultValue and defaultChecked typings (#3464, thanks @ilogico)As usual we recommend all users to upgrade.
As usual we recommend all users to upgrade.
compat is triggered in new jsx-runtime (#3445, thanks @Austaras)fix effect ordering (#3416, thanks @JoviDeCroock)
Due to a bug in the build of 10.6.3 we're republishing this was discovered in Astro
Due to a bug in the build of 10.6.3 we're republishing this was discovered in Astro
Fire useEffect in reverse component depth order (#3354, thanks @developit)
Handle hooks that throw during cleanup (#3345, thanks @JoviDeCroock)
Fix switch check for excessDomChildren (fixes IE11) (#3342, thanks @JoviDeCroock)
Adjust raf types (#3323, thanks @JoviDeCroock)
Skip rendering contents on the client in compat (#3238, thanks @developit)
focusin & focusout to type definitions (#3257, thanks @boarwell)onBeforeInput attribute to type definitions (#3256, thanks @boarwell)Compat: Add flushSync (#3094, thanks @zephraph)
flushSync (#3094, thanks @zephraph)react-spring error caused by augmented function contexts (#3165, thanks @developit)Array.prototype.slice.call for children arguments (#3143, thanks @fzzle)vnodeId generation (#2978, thanks @developit)children if null is provided as an argument (#3091, thanks @clyfish)i to reduce size (-32b) (#3193, thanks @liuarui)undefined initializer case for useState type (#3185, thanks @rschristian)deno compatible (#3079, thanks @lucacasonato)event.this to be never (#3147, thanks @JoviDeCroock)lazy to reflect behavior (#3139, thanks @JonasKruckenberg)forwardRef (#3144, thanks @cmlenz)useRef typing to include undefined when called without initial value (#3056, thanks @cmlenz)karma-esbuild to support M1 chips (#3153, thanks @marvinhagemeister)performance.now (#3130, thanks @developit)Fix unable to reset tabIndex (#3062 + #3064, thanks @marvinhagemeister)
tabIndex (#3062 + #3064, thanks @marvinhagemeister)compat/server (#3059 + #3061, thanks @marvinhagemeister)bigint numbers (#3010, thanks @marvinhagemeister)decoding attribute (#3054, thanks @sumanthratna)SVGFEFunc types (#3043, thanks @rschristian)options.unmount instead of overriding component.componentWillUnmount (#2919, thanks @tanhauhau)This release includes an enhancement to our devtools integration. Via a babel plugin you can get more readable hook names to show up in devtools inste
This release includes an enhancement to our devtools integration. Via a babel plugin you can get more readable hook names to show up in devtools instead of dozens of useState hooks in devtools. Check out https://github.com/preactjs/babel-plugin-transform-hook-names for more information.
Before:
After:
defaultValue not applied when value==null/undefined (#2957, thanks @marvinhagemeister)console.logs (#2959, thanks @marvinhagemeister)Fix unable to set contentEditable to false (#2938, #2939 thanks @marvinhagemeister + @developit)
contentEditable to false (#2938, #2939 thanks @marvinhagemeister + @developit)compat/jsx-dev-runtime missing in package (#2931, thanks @marvinhagemeister)createElement instead of just is [-5b] (#2943, thanks @developit)xlink: (#2939, thanks @developit)react to preact/compat (#2946, thanks @marvinhagemeister)workflow_dispatch and restructure branch trigger (#2933, thanks @andrewiggins)Fix contentEditable throwing when set to undefined/null (#2927, thanks @marvinhagemeister)
contentEditable throwing when set to undefined/null (#2927, thanks @marvinhagemeister)jsx-dev-runtime package entry (#2920, thanks @sventschui)Suspense (#2917, thanks @andrewiggins)Portal test not cleaning up container (#2925, thanks @marvinhagemeister)sinon dependencies to fix IE11 Promise error (#2922, thanks @marvinhagemeister)karma benchmarks not compiling (#2914, thanks @marvinhagemeister)babel-loader (#2911, thanks @andrewiggins)Happy New Year to everyone 🎉 Let's kick of the year with a few welcome bug fixes regarding preact/compat 👍
Happy New Year to everyone 🎉 Let's kick of the year with a few welcome bug fixes regarding preact/compat 👍
memo (#2896, thanks @andrewiggins)This is probably the last release for 2020. Thank you everyone for the amazing contributions over the year and we can't wait to see where Preact is go
This is probably the last release for 2020. Thank you everyone for the amazing contributions over the year and we can't wait to see where Preact is going in 2021! Lot's of interesting ideas are being worked on 🎉
_nextDom pointer if it is being unmounted (#2889, thanks @andrewiggins)/compat/server exports to work with import/export syntax and webpack 5 (#2873, thanks @SomethingSexy)useMemo/useCallback hooks (#2870, thanks @afzalsayed96)VNode type (#2855, thanks @andrewiggins)ref not being removed from props with jsx-runtime (#2840, thanks @marvinhagemeister)onchange to oninput for input[type=range] in IE11 (#2817, thanks @gcraftyg)JSDoc comments (#2883, thanks @andrewiggins)CSSStyleDeclaration for CSS property list (#2869, thanks @developit)readonly attribute (#2868, thanks @rschristian)defaultValue for select tag (#2848, thanks @Rafi993)_original typings (#2830, thanks @marvinhagemeister)csstype from version 2 to version 3 (#2829, thanks @bz2)vnode re-use detection (-2 B) (#2863, thanks @marvinhagemeister)diffChildren (#2813, thanks @JoviDeCroock)createRoot API (#2866, thanks @andrewiggins)FUNDING.yml (#2861, thanks @JoviDeCroock)devDependencies (#2856, thanks @andrewiggins)Fix compat/jsx-runtime missing in npm package (#2827, thanks @marvinhagemeister)
compat/jsx-runtime missing in npm package (#2827, thanks @marvinhagemeister)Throw when hook is invoked outside of render (#2816, thanks @marvinhagemeister)
jsx-runtime work when using an alias (#2805, thanks @JoviDeCroock)animateTransform tag type (#2822, thanks @HuHguZ)displayName to context (#2820, thanks @max-voronov)style attribute types (#1797, thanks @mnkhouri)jsx-runtime, fixes TS4.1 compatibility (#2811, thanks @ddprrt)ref types' current property non-optional (#2803, thanks @ivantm)vnode reference and golf implementation (#2517, thanks @JoviDeCroock)Reset hooks state when Suspense triggers (#2796, thanks @JoviDeCroock)
Suspense triggers (#2796, thanks @JoviDeCroock)_tl;dr: Bug-Fix only release that should get rid of the last className edge cases. We encourage everyone to upgrade._
tl;dr: Bug-Fix only release that should get rid of the last className edge cases. We encourage everyone to upgrade.
Despite our effort to account for all edge cases regarding className handling in preact/compat, we got some reports of some missed ones. This release corrects those :tada:
This release contains a fix to increase compatibility with next.js that ensures that the error overlay will show up.
className descriptors on Element vnodes (#2786, thanks @developit)className over class (#2782, thanks @JoviDeCroock)This release fixes a regression in regards to class/className handling in preact/compat. We encourage everyone to upgrade.
This release fixes a regression in regards to class/className handling in preact/compat. We encourage everyone to upgrade.
className normalization (#2774, thanks @JoviDeCroock)Promises in Suspense (#2776, thanks @kitten)check-export-map (#2777, thanks @marvinhagemeister)Fix incorrect package export for jsx-runtime (#2769, thanks @marvinhagemeister)
jsx-runtime (#2769, thanks @marvinhagemeister)Fix publishing error with jsx-runtime package (#2767, thanks @johakr)
jsx-runtime package (#2767, thanks @johakr)This has been a long time in the making for various virtual-dom based frameworks. Historically JSX was always transpiled to createElement function cal
This has been a long time in the making for various virtual-dom based frameworks. Historically JSX was always transpiled to createElement function calls.
// input
<div>foobar</div>
// output, we need to move "foobar" to `props.children`
createElement("div, {}, "foobar");
While this has served us well and is very reliable, it has proven to be hard to optimize. Most of the things we do in our createElement function could by done by babel directly, thereby making it smaller and faster. This is very desirable for us as this function is called a lot in any application. It's part of the so-called hot-path.
And that's exactly what the new signature does. It removes the need for us to pull out key from props, add back children to props and just makes the implementation simpler. As a nice benefit users won't need to manually import h/createElement anymore :tada:
// input
<li key="foo">foobar</li>
// output
jsx("li", { children: "foobar" }, "foo");
Usage with babel:
// babel.config.js
module.exports = {
plugins: [
["@babel/plugin-transform-react-jsx", {
runtime: "automatic", // defaults to classic (classic == createElement calls)
importSource: "preact", // NOT preact/jsx-runtime
}]
]
}
Note that the JSX transformer in TypeScript is a work in progress and will likely be released as part of version 4.1. We're currently running into https://github.com/microsoft/TypeScript/issues/40502 though, so the JSX typings are not found.
jsx-runtime support (#2764, thanks @JoviDeCroock, @marvinhagemeister)0 (#2757, thanks @marvinhagemeister)contenteditable (#2701, thanks @sventschui)style (#2723, thanks @JoviDeCroock)_suspended (#2765, thanks @marvinhagemeister)Your coding agent can read these notes before it upgrades. Set up the MCP server →