Preact 10


  • Switch back to microticks for scheduling as the default (#3911, thanks @marvinhagemeister , @JoviDeCroock)

Bug Fixes

  • Fix hydrating <textarea> with value prop (#3891, thanks @andrewiggins)
  • Prevent memory leak when creating and destroying root nodes by clearing currentComponent (#3908, thanks @JoviDeCroock)
  • Fix regression in 10.12.1 from #3889 which could lead to a state not updating after a context update was enqueued (#3906, thanks @JoviDeCroock)
  • Fix tests assertions for IE11 (#3912, thanks @marvinhagemeister)


  • Add indeterminate property (#3837, thanks @rschristian)
  • Add aria role attribute values (#3904, thanks @shoonia)
  • Add missing SVG Elements to types (#3905, thanks @shoonia)
  • Add Missing MemoExoticComponent type in preact/compat (#3898, thanks @rschristian)
  • Add ARIA attribute types (#3910, thanks @andrewiggins)

Bug Fixes

  • Fix non-numeric numbers passed to width or height attribute not working correctly (#3888, thanks @JoviDeCroock)
  • Fix createContext update being blocked by shouldComponentUpdate (#3889, thanks @marvinhagemeister)


  • Use microtick outside of events (#3879, thanks @JoviDeCroock)
  • Re-sort rerender queue if modified while we are processing rerenders (#3871, thanks @andrewiggins)
  • Preserve event handler return values (#3812, thanks @developit)

Bug Fixes

  • Fix: avoid bailing in strict equality (#3884, thanks @JoviDeCroock)
  • When unmounting, continue with the last DOM element's nextSibling (#3878, thanks @andrewiggins)
  • Fix parameters or JSX dev runtime (#3880, thanks @wooorm)
  • Improve Fragment unmounting while correctly swapping nested fragments (#3875, thanks @andrewiggins)
  • Clear css properties when passed undefined (#3862, thanks @andrewiggins)
  • Debug: limit "object as children" error to elements (#3801, thanks @developit)
  • Invoke setState callbacks setup in componentWillMount (#3806, thanks @andrewiggins)


  • add exactOptionalPropertyTypes support to html attributes (#3868, thanks @deadem)
  • Changes EventHandler<...> to have a this of type void. (#3867, thanks @MicahZoltu)
  • Narrows type of parent in render functions. (#3863, thanks @MicahZoltu)


  • Improve suspense test spies (#3856, thanks @andrewiggins)
  • docs: Fix a few typos (#3844, thanks @timgates42)
  • docs: Removes recommendation for preact-cli (#3816, thanks @rschristian)

Bug Fixes

  • Add an explicit default export for compatibility with esbuild (#3783, thanks @Verseth)
  • Fix useId uniqueness with shared parents + DOM nodes in between (#3773, thanks @marvinhagemeister)
  • Fix case where keyed children would get removed (#3779, thanks @JoviDeCroock)
  • Use in useSyncExternalStore (#3776, thanks @zalishchuk)


  • Consolidate benchmark workflow steps into a single reusable workflow (#3782, thanks @andrewiggins)
  • Upgrade bench dependencies (#3778, thanks @andrewiggins)
  • Upgrade workflow actions (#3777, thanks @andrewiggins)

Bug Fixes

  • Fix setState order (#3763, thanks @JoviDeCroock)
  • Fix duplicate ids with useId when Fragments are involved (#3758, thanks @marvinhagemeister)


  • Fix props not spreadable to <input> elements (#3764, thanks @mwszekely)

Bug Fixes

  • Fix webpack error when trying to import compat/package.json (#3755, thanks @akselander)
  • Fix nested fragments swapped incorrectly on conditional swap (#3738, thanks @JoviDeCroock)
  • Avoid synchronously adding setState callbacks (#3743, thanks @JoviDeCroock)
  • Fix signals not supported in HTML + SVG TypeScript definitions (#3747, thanks @marvinhagemeister)
  • Only remove nested DOM elements on unmount when necessary (#3741, thanks @developit)
  • Don't discard prop updates when nested state update is immediately cancelled (#3739, thanks @JoviDeCroock)
  • Align TypeScript definitions from react to refs and forward refs (#3713, thanks @PodaruDragos)
  • Add missing "types" field for preact/debug (#3732, thanks @marvinhagemeister)
  • Fix falsy data attributes not working (#3720, thanks @JoviDeCroock)
  • Ensure _mask property always has the same name in distributed version (#3721, thanks @JoviDeCroock)


New Hook: useId

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)


  • Fix memory leak by cleaning up _parent, _dom and __hooks after unmount (#3709, thanks @JoviDeCroock)
  • Fix case where the ref property could be omitted from reused VNodes (#3696, thanks @JoviDeCroock)
  • Pass errorInfo to useErrorBoundary callback (#3689, thanks @marvinhagemeister)
  • Fix typescript definition for class | className (#3711, thanks @PodaruDragos)


  • Fix the mac arm build (#3697, thanks @gengjiawen)
  • Fix published JS formats after #3697 (#3702, thanks @rschristian)
  • Add todo benchmark and add a proxy package that uses preact/hooks (#3708, thanks @JoviDeCroock)
  • Add deprecation notice to render()'s replaceNode argument (#3700, thanks @rschristian)
  • Improve types for bare createElement() and h() calls (#3690, thanks @JoviDeCroock)
  • Add test for useId (#3716, 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)
  • ensure we iterate over all hooks (#3675, thanks @JoviDeCroock)


  • fix hooks calling shouldComponentUpdate without context (#3671, thanks @developit)
  • fix case where we set SCU multiple times (#3670, thanks @JoviDeCroock)
  • fix sync-external-store with zustand (#3663, thanks @JoviDeCroock)


  • fix useSyncExternalStore relying on changed render values (#3655, thanks @JoviDeCroock)
  • avoid crashing due to __hooks being null (#3651, thanks @JoviDeCroock)

Bug Fixes

  • Fix infinite loop in radix-ui which enqueues multiple state updates in the same tick (#3645, thanks @JoviDeCroock )
  • Fix effects run for suspended components in rare instances (#3643, thanks @JoviDeCroock )
  • Fix useSyncExternalStore not working with function values (#3633, thanks @marvinhagemeister )
  • Defer bailing out of updates to the render phase to align with React (#3621 + #3623, thanks @JoviDeCroock )
  • Fix some SVG attributes applied with wrong casing (#3615, thanks @iminside)


  • Update esbuild (#3630, thanks @marvinhagemeister )
  • Make demo compatible with node 16 and 18 (#3617, @gengjiawen )


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:


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)


  • Adding types for 'part' attribute (#3595, thanks @rschristian)
  • prevent _suspended and _force from colliding (#3585, thanks @JoviDeCroock)


  • Add support for svg property shape-rendering (#3577, thanks @DannyvanderJagt)
  • Remove setState on unmounted component warning (#3576, thanks @ekwoka)


  • Fix allow return undefined in useMemo after skipped render (#3580, thanks @marvinhagemeister)
  • Fix incorrect useMemo return value after skipped render (#3579, thanks @marvinhagemeister)
  • Commit hooks in options.diffed (#3578, thanks @JoviDeCroock)
  • restrict "oninputCapture" conversion to just "oninput" vs "oninput*" (#3573, thanks @jramanat-oracle)


  • Improve unit tests to cover fix on #3573 (#3574, thanks @marconi1992)
  • Add _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)
  • Ensure both onchange and oninput callbacks are executes when typing (#3562, thanks @marconi1992)
  • Make createRoot / hydrateRoot compatible with React spec (#3560, thanks @3846masa)
  • Implement state settling in X (#3553, thanks @JoviDeCroock)


  • Fix size CI failing on node version missmatch (#3563, thanks @JoviDeCroock)


  • Add server.browser.js (#3544, thanks @JoviDeCroock)
  • Add textPath SVG type (#3546, thanks @backmeupplz)


  • improve compat types (#3534, thanks @JoviDeCroock)
  • support nodenext in TypeScript 4.7 (#3513, thanks @ilogico)
  • add missing containerInfo to portals (#3508, thanks @JoviDeCroock)


  • fix cleanup of debounceRendering hook after exceptions (#3530, thanks @robertknight)


  • fix Snyk sponsor link (#3533, thanks @developit)
  • add Snyk to sponsors (#3532, thanks @developit)
  • remove usages (#3528, thanks @mhmdanas)
  • fixes broken Slack shield in README (#3516, thanks @schalkventer)


  • Add new react-dom/client entry to compat (#3506, thanks @JoviDeCroock)


  • Simplify forwardRef and fix empty ref object (#3497, thanks @developit)


  • Add support for errorInfo (#3452, thanks @marvinhagemeister )
  • Allow for passing in multiple refs through forwardRef (#3494, thanks @JoviDeCroock)

Bug Fixes

  • Fix Incorrect translation of xlink:href attribute -> hhref (#3453, thanks @pguilbert)
  • Fix swapped jsx runtime __source and __self arguments (#3459, thanks @marvinhagemeister)
  • Ensure consistent format order for package entries (#3489, thanks @marvinhagemeister)
  • Reset useImperativeHandle ref to null when the component get unmounted (#3487, thanks @deadem)


  • Add missing defaultValue and defaultChecked typings (#3464, thanks @ilogico)


  • Fix test not cleaned up properly (#3457, thanks @marvinhagemeister
  • Fix fake timers not being reset on test failure (#3458, thanks @marvinhagemeister)
  • Remove unused devDependency (#3460, thanks @marvinhagemeister)
  • Update test dependencies (#3467, thanks @marvinhagemeister)
  • Improve karma test console logging (#3475, thanks @marvinhagemeister)
  • Fix karma error traces not being source mapped (#3476, thanks @marvinhagemeister)

As usual we recommend all users to upgrade.

Bug Fixes

  • Fix cursor reset in Safari in input field while typing by (#3448, #3450, thanks @marvinhagemeister)
  • Make sure compat is triggered in new jsx-runtime (#3445, thanks @Austaras)


  • fix effect ordering (#3416, thanks @JoviDeCroock)
  • Normalize CompositionEvent listeners in preact/compat (#3430, thanks @hpneo)


  • Change type of for better TypeScript compatibility with @emotion/react and @types/react. (#3431, thanks @rolftimmermans)


  • Use onInput instead of onChange in the README example (#3420, thanks @matthiask)
  • remove malfunctioning csb ci (#3417, thanks @JoviDeCroock)
  • Fix instructions for npm tag in CONTRIBUTING (#3380, thanks @andrewiggins)
  • Fix typo in release workflow (#3379, thanks @andrewiggins)
  • Automate building npm package for release (#3378, thanks @andrewiggins)

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)
  • Map onFocus/onBlur to onfocusin/onfocusout (#3355, thanks @developit)


  • Add test for useEffect ordering with useMemo (#3360, thanks @andrewiggins)


  • Handle hooks that throw during cleanup (#3345, thanks @JoviDeCroock)
  • Include all package.json files in the export-maps (#3344, thanks @developit)


  • Fix switch check for excessDomChildren (fixes IE11) (#3342, thanks @JoviDeCroock)


  • Fix es5 warnings in local test runs (#3340, thanks @developit)


  • Adjust raf types (#3323, thanks @JoviDeCroock)
  • Suggest fix for useRef-types in hooks (#3222, thanks @JoviDeCroock)
  • Add component props to compat exports (#3321, thanks @JoviDeCroock)


  • Fix className leak (#3279, thanks @JoviDeCroock)
  • Support hydrating html comments (#3327, thanks @JoviDeCroock)


  • Fix tests in IE11 (#3264, thanks @developit)
  • Add jsx(-dev)-runtime to the export maps (#3320, thanks @JoviDeCroock)


  • Skip rendering contents on the client in compat (#3238, thanks @developit)
  • Nested Suspended trees may be missing _children (#3260, thanks @developit)
  • Manage camel-cased dominant-baseline attribute in preact/compat (#2859, thanks @nmondon)
  • Restrict camelCase prop renaming to non-custom elements (#3259, thanks @jramanat-oracle)
  • Fix bug in chrome where select values continuously rerender (#3226, thanks @JoviDeCroock)
  • Avoid leaking internal vnodes (#3106, thanks @developit)


  • Fix ref typing of forwardRef (#3214, thanks @itkrt2y)
  • Add focusin & focusout to type definitions (#3257, thanks @boarwell)
  • Add onBeforeInput attribute to type definitions (#3256, thanks @boarwell)
  • Add missed HTML attributes in the TS typings (#3246, thanks @rschristian)


  • Update babel.config.js (#3265, thanks @RRDAWLX)
  • Delete redundant previousComponent in hooks (#3271, thanks @RRDAWLX)
  • Update test dependencies (#3255, thanks @marvinhagemeister)


  • Compat: Add flushSync (#3094, thanks @zephraph)

Bug Fixes

  • Prevent eager child removal (#3210, thanks @JoviDeCroock)
  • Compat: Update Fake React Version (#3189, thanks @tim-on-github)
  • Fix react-spring error caused by augmented function contexts (#3165, thanks @developit)
  • Use for children arguments (#3143, thanks @fzzle)
  • Improve performance of vnodeId generation (#2978, thanks @developit)
  • Fix: should override children if null is provided as an argument (#3091, thanks @clyfish)


  • refactor(diff-index): reuse i to reduce size (-32b) (#3193, thanks @liuarui)
  • Simplify unmount logic (#3120, thanks @andrewiggins)


  • Fix type definitions (#3191, thanks @craftedsystems)
  • Fix undefined initializer case for useState type (#3185, thanks @rschristian)
  • Make typings deno compatible (#3079, thanks @lucacasonato)
  • Change typing of event.this to be never (#3147, thanks @JoviDeCroock)
  • Update signature of lazy to reflect behavior (#3139, thanks @JonasKruckenberg)
  • Improve typing of forwardRef (#3144, thanks @cmlenz)
  • Fix the useRef typing to include undefined when called without initial value (#3056, thanks @cmlenz)


  • Docs: Removing suggestion to install Preact CLI (#3204, thanks @rschristian)
  • Upgrade karma-esbuild to support M1 chips (#3153, thanks @marvinhagemeister)
  • Rely directly on (#3130, thanks @developit)
  • Add The Guardian as a Github backer (#3086, thanks @mchv)
  • Fix typo in issue template (#3067, thanks @rschristian)

Bug Fixes

  • Fix unable to reset tabIndex (#3062 + #3064, thanks @marvinhagemeister)
  • Add ESM entry for compat/server (#3059 + #3061, thanks @marvinhagemeister)
  • Fix unable to render bigint numbers (#3010, thanks @marvinhagemeister)
  • Fix reordering issue of memoized component when the component initially render null (#2988, thanks @tanhauhau)


  • Add decoding attribute (#3054, thanks @sumanthratna)
  • Add missing SVGFEFunc types (#3043, thanks @rschristian)


  • Update issue templates (#3058, thanks @marvinhagemeister)
  • Update esbuild to natively support Apple's M1 chip (#3028, thanks @marvinhagemeister)
  • Create separate trace log directories per benchmark (#3024, thanks @andrewiggins)
  • Reduce redundant preparation in bench scripts (#3013, thanks @andrewiggins)
  • Tests: Fix stale watch cache (#3012, thanks @marvinhagemeister)
  • Tests: Reduce CPU usage in watch mode (#3011, thanks @marvinhagemeister)
  • Tests: Fix Chrome 88 stack traces displayed wrong in terminal (#3008, thanks @marvinhagemeister)
  • Upgrade tachometer and add script to analyze browser trace logs (#3005, thanks @andrewiggins)
  • Use options.unmount instead of overriding component.componentWillUnmount (#2919, thanks @tanhauhau)
  • Update esbuild + karma-esbuild (#2991, thanks @marvinhagemeister)
  • Add a few minor tests (#2981, thanks @43081j)

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 for more information.





Bug Fixes

  • Compat: Fix defaultValue not applied when value==null/undefined (#2957, thanks @marvinhagemeister)
  • Fix HTML comments breaking hydration (#2956, #2960 thanks @marvinhagemeister and @developit)


  • Tests: Improve console.logs (#2959, thanks @marvinhagemeister)
  • Remove redundant benchmark initialization (#2966, thanks @andrewiggins)
  • Fix pr-reporter condition (#2964, thanks @andrewiggins)
  • Report benchmarks on PRs from forks (#2961, thanks @andrewiggins)
  • Update size action to only run when src files change (#2962, thanks @andrewiggins)
  • Use artifact flow to report benchmarks results (#2953, thanks @andrewiggins)

:chart_with_upwards_trend: Bug Fixes

  • Fix unable to set contentEditable to false (#2938, #2939 thanks @marvinhagemeister + @developit)
  • Fix compat/jsx-dev-runtime missing in package (#2931, thanks @marvinhagemeister)

:golf: Code golfing

  • Golf element diffing [-17b] (#2942, thanks @developit)
  • Pass all props to createElement instead of just is [-5b] (#2943, thanks @developit)
  • Use try/catch for properties instead of an allow list, remove xlink: (#2939, thanks @developit)
  • Simplify diffChildren's handling of excessDomChildren and oldDom (#2941, thanks @andrewiggins)

:hammer_and_wrench: Maintenance

  • Tests: Alias react to preact/compat (#2946, thanks @marvinhagemeister)
  • Separate benchmarks into their own workflow (#2944, thanks @andrewiggins)
  • Simplify top-level render and remove dead code (#2940, thanks @andrewiggins)
  • Tests: Fix stack traces not sourcemapped (#2935, thanks @marvinhagemeister)
  • Fail build on missing compat entries in files entry (#2934, thanks @marvinhagemeister)
  • Add workflow_dispatch and restructure branch trigger (#2933, thanks @andrewiggins)
  • Tests: Fix vscode breakpoints not being hit (#2932, thanks @marvinhagemeister)
  • Fix incorrect sourcemap line mapping in tests (#2929, thanks @marvinhagemeister)

Bug Fixes

  • Fix contentEditable throwing when set to undefined/null (#2927, thanks @marvinhagemeister)
  • Compat: Add basic shims for some scheduler functions (#2912, thanks @marvinhagemeister)
  • Add jsx-dev-runtime package entry (#2920, thanks @sventschui)
  • Avoid overriding render method in Suspense (#2917, thanks @andrewiggins)
  • Fix updating suspended component (#2910, thanks @tanhauhau)


  • Fix Portal test not cleaning up container (#2925, thanks @marvinhagemeister)
  • Fix tests in IE11 (#2924, thanks @marvinhagemeister)
  • Fix karma IE11 target containing ES2015 syntax (#2923, thanks @marvinhagemeister)
  • Upgrade sinon dependencies to fix IE11 Promise error (#2922, thanks @marvinhagemeister)
  • Fix SauceLabs runner (#2918, thanks @andrewiggins)
  • Fix karma benchmarks not compiling (#2914, thanks @marvinhagemeister)
  • Remove babel-loader (#2911, thanks @andrewiggins)
  • Remove unused comment (#2909, thanks @marvinhagemeister)

Happy New Year to everyone 🎉 Let's kick of the year with a few welcome bug fixes regarding preact/compat 👍

Bug Fixes

  • Fix bug when re-rendering a suspended hydration node (#2903, thanks @andrewiggins)
  • Fix devtools error with portals (#2904, thanks @JoviDeCroock)
  • Fix child reordering with memo (#2896, thanks @andrewiggins)


  • Replace webpack with esbuild + babel in karma test runner(#2907, thanks @marvinhagemeister)
  • Rename "coverage" env to "COVERAGE" (#2906, thanks @marvinhagemeister)
  • Ensure expression is not marked for removal (#2905, thanks @marvinhagemeister)
  • Prepare test code to be run in ES2015+ environments (#2901, thanks @marvinhagemeister)
  • Fix invalid jsx comment (#2902, thanks @marvinhagemeister)
  • Fix console.log not showing up in browser (#2900, thanks @marvinhagemeister)
  • Improve hydration tests (#2899, thanks @andrewiggins)
  • Add support for pretty printing karma console logs (#2893, thanks @marvinhagemeister)

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 🎉

Bug Fixes

  • Correct _nextDom pointer if it is being unmounted (#2889, thanks @andrewiggins)
  • Updating /compat/server exports to work with import/export syntax and webpack 5 (#2873, thanks @SomethingSexy)
  • Remove arguments warning for useMemo/useCallback hooks (#2870, thanks @afzalsayed96)
  • Use strict equality checks when comparing VNode type (#2855, thanks @andrewiggins)
  • Fix ref not being removed from props with jsx-runtime (#2840, thanks @marvinhagemeister)
  • Compat: Don't convert onchange to oninput for input[type=range] in IE11 (#2817, thanks @gcraftyg)


  • Improve JSDoc comments (#2883, thanks @andrewiggins)
  • Use CSSStyleDeclaration for CSS property list (#2869, thanks @developit)
  • Add readonly attribute (#2868, thanks @rschristian)
  • Add defaultValue for select tag (#2848, thanks @Rafi993)
  • Update _original typings (#2830, thanks @marvinhagemeister)
  • Update csstype from version 2 to version 3 (#2829, thanks @bz2)


  • SimplifyPortal implementation (#2890, thanks @marvinhagemeister, @developit)
  • Simplify vnode re-use detection (-2 B) (#2863, thanks @marvinhagemeister)
  • Save 6 Bytes (#2844, thanks @marvinhagemeister)
  • Move child reodering to diffChildren (#2813, thanks @JoviDeCroock)


  • Add discussions and a csb template (#2875, thanks @JoviDeCroock)
  • In benches, abstract framework differences with createRoot API (#2866, thanks @andrewiggins)
  • Add filter benchmark (#2851, thanks @marvinhagemeister)
  • Update benchmarks (#2865, thanks @andrewiggins)
  • Add preactjs-gh-sponsor account to FUNDING.yml (#2861, thanks @JoviDeCroock)
  • Upgrade devDependencies (#2856, thanks @andrewiggins)
  • Add package tags (#2841, thanks @developit)
  • Trial codesandbox-ci (#2819, thanks @JoviDeCroock)
  • Fix compat/jsx-runtime missing in npm package (#2827, thanks @marvinhagemeister)

Bug Fixes

  • Throw when hook is invoked outside of render (#2816, thanks @marvinhagemeister)
  • Make jsx-runtime work when using an alias (#2805, thanks @JoviDeCroock)
  • Allow rendering from an effect (#2804, thanks @JoviDeCroock)


  • Add animateTransform tag type (#2822, thanks @HuHguZ)
  • Add displayName to context (#2820, thanks @max-voronov)
  • Improve style attribute types (#1797, thanks @mnkhouri)
  • General JSX types export for jsx-runtime, fixes TS4.1 compatibility (#2811, thanks @ddprrt)
  • Make ref types' current property non-optional (#2803, thanks @ivantm)


  • Remove circular vnode reference and golf implementation (#2517, thanks @JoviDeCroock)
  • Add hydration suspend tests (#2755, thanks @andrewiggins)
  • Fix prototype spies not being reset in tests (#2823, thanks @marvinhagemeister)

Bug Fixes

  • Reset hooks state when Suspense triggers (#2796, thanks @JoviDeCroock)
  • Run parent effects when child throws (#2794, thanks @sventschui)


  • Fix memo function types definition (#2793, thanks @dzhykaiev)

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.

Bug Fixes

  • Emit error event for errors handled by an error boundary (#2784, thanks @sventschui)
  • Fix className descriptors on Element vnodes (#2786, thanks @developit)
  • Give precedence to 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.

Bug Fixes

  • Fix className normalization (#2774, thanks @JoviDeCroock)
  • Fix edge cases for thrown Promises in Suspense (#2776, thanks @kitten)


  • Replace custom script with check-export-map (#2777, thanks @marvinhagemeister)
  • Upgrade bench infra and add tachometer PR reporter (#2775, thanks @andrewiggins)
  • Optimizations for preact/jsx-runtime (#2771, thanks @developit)
  • Fix incorrect package export for jsx-runtime (#2769, thanks @marvinhagemeister)
  • Fix publishing error with jsx-runtime package (#2767, thanks @johakr)

New JSX-runtime functions

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

// 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 though, so the JSX typings are not found.


  • Add jsx-runtime support (#2764, thanks @JoviDeCroock, @marvinhagemeister)
  • Implement Suspend & Resume for both hydration and new tree construction (#2754, thanks @developit)

Bug Fixes

  • Fix unable to set progress value to 0 (#2757, thanks @marvinhagemeister)
  • Fix capturing and non-capturing listeners on the same element (#2740, thanks @devongovett)
  • Prevent cursor jumps inside contenteditable (#2701, thanks @sventschui)
  • Add length check for hooks dependency array (#2729, thanks @JoviDeCroock)
  • Fix rendering children as zero number (#2725, thanks @JiLiZART)
  • Avoid assigning to readonly style (#2723, thanks @JoviDeCroock)


  • Add mangle key for _suspended (#2765, thanks @marvinhagemeister)
  • Compat Optimizations (v11 backport) (#2752, thanks @developit)
  • Add version check to issue template (#2731, thanks @marvinhagemeister

tl;dr: A good handful of bug fixes make this release the ideal candidate to upgrade! Should be very safe to upgrade

Thanks to all the people who made this release possible! This is for everyone who took part in our discussions, helped report issues, did code contributions or just spread the word. Thank you all for another amazing release :raised_hands:


  • Remove all the bytes (#2665, thanks @developit)

Bug Fixes

  • Fix state turned readonly in update fn (#2717, thanks @marvinhagemeister)
  • Fix deprecation warnings flooding console with preact/debug (#2711, thanks @marvinhagemeister)
  • Initialize normalized props with undefined instead of null in preact/compat (#2695, thanks @aralroca)
  • Add React "secret or fired" shim for react-relay (#2692, thanks @marvinhagemeister)
  • Properly unset href on nullish value (#2693, thanks @marvinhagemeister)
  • Go back to undefined as default value for useRef (#2689, thanks @marvinhagemeister)
  • preact/debug: Add component stack to prop type validation error, do not pass ref to prop-types validation (fixes mui incompatbility) (#2685, thanks @sventschui)


  • Automatically add dom lib (#2713, thanks @Gerrit0)
  • Update useDebugValue typings to align with React (#2699, thanks @leader22)
  • Remove useErrorBoundary compat types (#2631, thanks @38elements)
  • Add support for IntrinsicElements in ComponentProps (#2680, thanks @remcohaszing)


  • Update slack link in (#2702, thanks @marvinhagemeister)
  • Fix invite link (#2697, thanks @JoviDeCroock)

tl;dr: This is a bug-fix only release and safe to upgrade :tada:

This release contains some amazing fixes by first time contributors! Thank you so much for everyone who filed issues or contributed PRs :heart:

Bug Fixes

  • Avoid touching DOM-attributes during hydration (#2679, thanks @JoviDeCroock)
  • Fix incorrect download attribute handling (#2674, thanks @marvinhagemeister)
  • Allow the same component to be suspended multiple times (#2661, thanks @tanhauhau)
  • Allow resolve promise after suspense unmounted (#2664, thanks @tanhauhau)
  • Throw when hook is used inside effect (#2672, thanks @marvinhagemeister)
  • Fix incorrect text node handling in diffElementNodes (#2658, thanks @perseveringman)
  • Default useRef to null (#2648, thanks @JoviDeCroock)
  • Fix calling setState in constructor (#2640, thanks @sventschui)


  • Change argument of useRef to optional (#2651, thanks @38elements)
  • Updated capture definition. (#2643, thanks @JonathanBristow)
  • Update preact/compat types (#2628, thanks @jeremy-coleman)


  • Run benches on linux VMs (#2595, thanks @andrewiggins)
  • Fix package.json "authors" field (#2635, thanks @developit)

tl;dr: This is a bug-fix only release and safe to upgrade :tada:

We've landed some very anticipated fixes and therefore thought to cut a new release not soon after :raised_hands:

Bug Fixes

  • Initialize children if they're null in suspense (#2570, thanks @sventschui)
  • Fix case where textarea doesn't reset state (#2615, thanks @JoviDeCroock)
  • Fix passing null to forwardRef (#2600, thanks @JoviDeCroock)


  • Make strokeMiterlimit accept a number as well (#2620, thanks @lfamorim)
  • Add feDropShadow definition (#2609, thanks @Somnid)


  • Add missing LICENSE files and fix submodule names (#2611, thanks @hbroer)
  • Fix typo in (#2603, thanks @futantan)
  • HTTPS fix for README image (#2602, thanks @developit)
  • Run same workflow on PR and master push (#2601, thanks @andrewiggins)

This is a bugfix-only release and updating is seamless. We encourage everyone to do so :+1:

Bug Fixes

  • Prevent Suspense from inadvertently modifying shared Component class (#2594, thanks @andrewiggins)
  • Fix Fragment edge case (#2551, thanks @JoviDeCroock)
  • Check for existence of requestAnimationFrame while cancelling effects it (#2573, thanks @Hydrophobefireman)
  • Handle setState in render (#2565, thanks @jamesb3ll)
  • Fix forwardRef passing object instead of null (#2567, thanks @marvinhagemeister)
  • Normalize value prop on textareas in compat (#2558, thanks @btk5h)
  • Prevent useReducer from mutating the previous returned result (#2550, thanks @JoviDeCroock)


  • Improve code coverage (#2596, thanks @andrewiggins)
  • Optimize unpkg entry: Commonjs plus globals default export (#2261, thanks @developit)
  • Share build output between action jobs (#2579, thanks @andrewiggins)
  • Add TypeScript for extending JSX types with custom elements (#2581, thanks @andrewiggins)
  • Update Node version in actions and format YAML files using prettier (#2577, thanks @andrewiggins)
  • Add GitHub action to run benches (#2560, thanks @andrewiggins)
  • Add many_updates benchmark (#2559, thanks @andrewiggins)
  • 🏌️‍♂️ rAF check (#2555, thanks @developit)
  • put rerenderCount on the Component prototype (#2552, thanks @JoviDeCroock)
  • Use "babel-plugin-transform-rename-properties" to consistently mangle properties (#2548, thanks @andrewiggins)

This is a hotfix for today's 10.4.2 release that:

  • addresses an issue with mobx-react (#2541, thanks @marvinhagemeister)
  • fixes a regression in useEffect cleanup callbacks (#2542, thanks @fuzetsu)

What a month we had! The weather is getting warmer and I think we can all enjoy a new refreshment in the form of a Preact release! The past weeks saw a good chunk of bug fixes and a bit of house keeping. Upgrades should be as straightforward as swapping out the version number in package.json and running npm install or yarn install once!

Introducing prefresh (experimental)

It was one of those miracle days where all the pieces fell into place just perfectly: @JoviDeCroock got a HMR (=hot module reloading) prototype up without any changes to Preact and running in a couple hours! With the biggset achievement being that it works amazingly well in keeping hooks state around.

Since then he was contacted by various maintainers of bundlers to collaborate on an ideal developer experience. Today, about a little more than a week later we have them ready to be tested. And we need your feedback to make it the best HMR experience we can! Please file any issue you come across!

Snowpack template

Oh and while we were at it @sventschui added a Preact template for snowpack! You can get it up and running via this line:

npx create-snowpack-app my-project --template @snowpack/app-template-preact


Preact sightings

Recently deno cut it's 1.0.0 release which is a huge achievement. It's a new spin on what node could look like if it would have started fresh in 2019 and we're excited where this experiment will lead to! Despite it being very early it made some waves in our community and we were filled with joy when we noticed that the website is built with our beloved framework!

Bug Fixes

  • Add noop React.StrictMode to compat (#2529, thanks @developit)
  • Use latest reducer function in useReducer (#2526, thanks @hadeeb)
  • Improve IS_NON_DIMENSIONAL for compatibility with animation-iteration-count (#2523, thanks @viko16)
  • Polyfill prototype setter in debug (#2514, thanks @JoviDeCroock)
  • Fix hydrate export in compat (#2511, thanks @hadeeb)
  • Fix creating multiple roots from useEffect (#2493, thanks @JoviDeCroock)


  • Add StrictMode to compat/src/index.d.ts (#2530, thanks @38elements)
  • Add loading attribute to HTMLAttributes TypeScript interface (#2521, thanks @gerardo-rodriguez)


  • Fix indentation (#2528, thanks @Vincent-Carrier)
  • Fix test on IE11 by wrapping the spy helper (#2524, thanks @JoviDeCroock)
  • Remove unnecessary cloning of props (#2516, thanks @hadeeb)
  • Iteratively diff children and convert array children to Fragments (#2507, thanks @andrewiggins)
  • Update focus tests to better match user behavior (#2506, thanks @andrewiggins)
  • IE11+ -> IE11 (#2499, thanks @38elements)
  • Reorganize render test file (#2487, thanks @andrewiggins)
  • golf effect cleanup (#2494, thanks @JoviDeCroock)
  • Remove processingException check (#2483, thanks @JoviDeCroock)
  • Reduce unnecessary DOM attribute reads (#2486, thanks @andrewiggins)
  • Remove unnecessary excessDomChildren creation (#2491, thanks @andrewiggins)
  • Add update benchmark from js-framework-benchmark (#2489, thanks @andrewiggins)

tl;dr: This release allows our devtools extension to inspect hooks. Apart from that it includes the usual round of bug fixes.

We have a nice little present for you and that is hooks are now fully supported in Preact Devtools 0.5.0 :tada: The extension is currently awaiting approval in browsers stores and your browser will automatically update to it in the following days.

Screenshot from 2020-04-19 21-10-06

Both @andrewiggins and @JoviDeCroock went full on bug hunting mode and got some neat fixes in! We also saw an awesome contribution from @davidje13 who found an error in our types for memo :+1:

But the true star from the show is without a doubt that we finally have updated our typings to bring back the marquee element. @developit himself took the honors and made sure that developers can continue to use this element in their demo applications.

Bug Fixes

  • Add support for hooks inspection via devtools (#2480, thanks @marvinhagemeister)
  • Make warn effectively warn instead of throwing (#2477, thanks @JoviDeCroock)
  • Support node13 (#2451, thanks @JoviDeCroock)
  • Use setProperty to set "value" and "checked" properties (#2472, thanks @andrewiggins)
  • Normalize props on cloneElement (#2469, thanks @JoviDeCroock)
  • Set value/checked on custom-elements (#2465, thanks @JoviDeCroock)
  • Add support for context displayName (#2454, thanks @marvinhagemeister)


  • Add <marquee> typings (#2466, thanks @developit)
  • Account for defaultProps when wrapping components with memo (#2461, thanks @davidje13)


  • Run benchmarks with v8, master, and local builds (#2475, thanks @andrewiggins)
  • Reduce number of builds in actions (#2476, thanks @andrewiggins)
  • Add npm prepare script (#2473, thanks @andrewiggins)
  • Upgrade devtools adapter (#2471, thanks @marvinhagemeister)
  • Tachometer benchmarks (#2462, thanks @andrewiggins)

tl;dr: This release contains some very amazing improvements to hydration and to performance when memoized vnodes are used. We recommend everyone to upgrade :tada:

We understand that the past weeks have been very strange for everybody across the world. We've spent that last weeks mainly focusing on making sure our families and loved ones are safe and taken care of. Whether you're using Preact at work or in a sideproject we hope that this release brings you a little bit of joy and makes your day a little bit brighter :stars:

Strictly equal vnodes bail out of render

When an vnode is equal to the one from the last render we will successfully bail out of rendering. This is a performance optimization many state libraries frequently make use of. The most well known of those is probably react-redux. We've wanted to add this for the initial Preact X release but had to postpone due to not having found the proper solution back then. In the following months we've passed around various ideas on how to best solve this and it wasn't until last month when it finally clicked and the pieces fell together. @JoviDeCroock had a prototype up running in a few days and has spent a lot of time into making sure that this performance optimization works in all scenarios. We can't stress enough how much of an accomplishment this is. This is really amazing work by @JoviDeCroock and we're over the moon that it has finally landed in Preact :tada:

hydrate falls back to render for new subtrees

When doing SSR there are cases where the DOM tree and the vnodes don't match. This can lead to a lot of problems, but for now we handle situations a bit more gracefully when there is no existing DOM node present. If we encounter that, we just opt out of hydration for that tree and revert back to doing a full diff. In the future we plan to add more warnings to preact/debug for that.


  • Implement strict-equality for vnodes (#2386, thanks @JoviDeCroock)
  • Fallback to render when subtrees are inserted on hydration (#2438, thanks @JoviDeCroock)
  • Add owner stack for invalid child warning (#2416, thanks @marvinhagemeister)

Bug Fixes

  • Fix error messages logged by failed prop type checks (#2434, thanks @robertknight)
  • Skip setProperty for known bypass cases (key, children) (#2213, thanks @developit)
  • Improve handling of exceptions thrown during act callback (#2433, thanks @robertknight)
  • Ensure ref is mutable even if a DOM node is inserted into it (#2422, thanks @JoviDeCroock)
  • Fix Context not re-rendering when reset to defaultValue (#2420, thanks @JoviDeCroock)
  • Disable normalization of string vnodes (#2410, thanks @JoviDeCroock)


  • Update act type signature (#2444, thanks @ddayguerrero)
  • Add _patchedLifecycles to compat/src/internal.d.ts (#2408, thanks @38elements)


  • Docs: Fix simple typo, stucture -> structure (#2440, thanks @timgates42)
  • Fix CONTRIBUTING typo (#2417, thanks @futantan)
  • Use double equal with typeof (#2409, thanks @polemius)
  • Enable saucelabs-connect (#2411, thanks @developit)
  • Move from Travis to Github Actions (#2401, thanks @JoviDeCroock)

This is a maintenance release, upgrading should be free, please do report it in case you encounter any issues.

render-queue sorting

Preact batches all rendering work and executes from the top of the Virtual DOM tree to the bottom. However, if new rendering tasks were added during an existing render, they were processed without regard for their depth in the tree.

Thanks to a clever fix from @jviide, Preact's render queue is now immutable. Any new tasks added during rendering are placed into a second batch.

material-ui integration

A peculiar issue users were seeing with material-ui related to a ref never getting populated, we went very deep into the codebase and found out that our forwardRef was a bit too eager, at creation it would already start forwarding. We moved this to a later point, now just before the vnode will get diffed the ref will be forwarded.


  • Fix mui Popover integration (#2403, thanks @JoviDeCroock)
  • Fix defaultValue with re-render (#2392, thanks @aralroca)
  • Sort new renderqueue items (#2396, thanks @jviide)


  • Fix stopPropagation override for IE11 (#2390, thanks @JoviDeCroock)
  • Use createEvent since InputEvent and new Event won't work in IE11 (#2400, thanks @JoviDeCroock)
  • Fix useErrorBoundary hook callback type (#2397, thanks @Sasha-Sorokin)
  • Update cloneElement typings (#2388, thanks @ddayguerrero)

tl;dr: Some minor changes which make this release safe to upgrade for everyone!

Another week, another Preact release! It seems like the previous one wasn't too long ago, but there are already so many cool changes in master that we're eager to bundle them in a neat release!

Much improved conditional rendering

One insanely cool and very important change was done by @andrewiggins, who woke up one day and found a very elegant solution to handling conditionally rendered elements. Most virtual-dom-based frameworks mark a falsy result with some sort placeholder (sometimes referred to as "holes"), so that the diffing algorithm can ensure that elements are not moved around needlessly.

It's not just for performance though as they are some real world consequences to moving nodes around. The most common annoyance is <input>-elements losing focus whenever a parent is moved. With this change we are pretty confident that we squashed all known issues on that front :+1:

Improved SVG attribute casing

SVG also received a big change by @steveharrison . It's his first contribution Preact and he already knocked it out of the park with an excellent PR. HE went through the whole SVG spec and noticed that we didn't match some of the weird casings of SVG-Attributes properly and his PR remedies that beautifully! :100:

Preact sightings

@pksjce finished her YouTube series where she reads through a portion of the Preact source code. With that she helped us tremendously in spotting areas in code which lacked comments and finally pushed us to create a proper "Contributing" guide. If you are considering contributing to Preact, this short document is well worth a read as it contains an overview of the repo's structure and answers for the most common questions regarding our code. That said if you feel like something is missing or you do have troubles understanding some sections of the source, please reach out to us! We're here to help and feedback about friction points is crucial to making Preact better for everyone :+1:

Checking our official website you may have noticed some slight changes here and there. They're mostly to simplify navigation or to give our docs a more fitting structure. The long term plan is to integrate our learnings about the most common support questions we get and fill in those spots. @NJalal7 spotted a few of those areas and even found a bug in Preact in the process that we we're promptly able to fix :raised_hands:

Thank you so much to everyone who contributed code, helped us in narrowing down issues or participated in making Preact even better. Preact wouldn't be were it is now without you all :heart:


  • Compat: Add isPropagationStopped fn to event (#2378, thanks @reznord)
  • Compat: Add isDefaultPrevented fn to event (#2377, thanks @teodragovic)
  • Large performance boost in preact/debug (#2362, thanks @developit)

Bug Fixes

  • Fix event.isPropagationStopped() (#2380, thanks @38elements)
  • Fixed SVG attribute names not being converted to the correct attribute names in the DOM. (#2366, thanks @steveharrison)
  • Fix incorrect warning on setState inside componentWillMount (#2367, thanks @marvinhagemeister)
  • Fix static hydration (#2365, thanks @JoviDeCroock)
  • Fix hook outside of component test (#2359, thanks @JoviDeCroock)
  • Remove warning for useEffect/useLayoutEffect (#2358, thanks @JoviDeCroock)
  • Improve null placeholder DOM placement (#2355, thanks @andrewiggins)


  • Fix options.event type definition (#2381, thanks @38elements)


  • Add comment to clarify seemingly redundant string concatenation (#2369, thanks @mhmdanas)
  • Rename some variables to increase legibility (#2361, thanks @andrewiggins)
  • Combine searches in excessDomChildren into the same code block (#2356, thanks @andrewiggins)
  • Fix failing test by defaulting to empty array (#2353, thanks @JoviDeCroock)
  • Add new test file for null placeholders (#2352, thanks @andrewiggins)

tl;dr: Another bug-fix only release. It's safe to upgrade and we encourage everybody to do so :+1:

It's sunny today and I haven't been outside yet, so I'll make it quick: Those pesky ref TypeScript errors when used with a CSS-in-JS library should be no more! So if you're using styled-components, emotion, goober or any other CSS-in-JS library, this update is for you! :100:

@robertknight found that false values where not special cased for aria-* attributes as they have a different way of treating boolean values compared to the DOM. Many boolean-like attributes like aria-checked have three states:

  • true element is checked
  • false element is unchecked, but it's possible to check it
  • undefined (default) element can't be checked

As usual thank you so much for everyone who helped make Preact better by contributing code or reporting issues! You all rock :+1:

Bug Fixes

  • Add support for false value in aria-attributes (#2347, thanks @marvinhagemeister)
  • Remove array provided to callback in and .forEach (#2326, thanks @mhmdanas)
  • Fix missing index (#2322, thanks @JoviDeCroock)


  • Match useErrorBoundary type with componentDidCatch (#2332, thanks @intrnl)
  • Fix incompatible ref typing with ReactElement (& popular react libraries) (#2099, thanks @xiel)
  • Make useErrorBoundary's callback param optional (#2320, thanks @intrnl)


  • Fix failing useImperativeHandle tests (#2346, thanks @marvinhagemeister)
  • Create (#2342, thanks @JoviDeCroock)
  • Node "exports" adjustments (#2327, thanks @guybedford)
  • Add package.json to package.exports (#2319, thanks @MylesBorins)

tl;dr: Just a minor bug-fix-only release. Safe to upgrade for everyone.

We've been notified of an issue with the way we used the new exports feature that was introduced with Node 13, so we wanted to get out patch release as quickly as possible. Despite that we managed to include several other fixes in such a short timeframe that are worthy to mention.

Run tests against the minfied production artifact

This is big for us. @andrewiggins did an amazing PR which modifies our testing infrastructure to execute the tests against the minfied production bundles that are published to npm. This greatly reduces any chances of us not catching bugs that may exist in transpilers or the custom minify config we're using. And Andre promptly found a few misconfigurations already. Most of you propably didn't run into these issues as they are somewhat in the edge case area, but it's amazing to have a tool to automatically check our code for an mishaps :100:

A new size bot

In the early days of the Preact X rewrite we made a promise to ourselves in that we would check the effects on size for each PR. We started by printing out all the sizes of our exports via microbundle, but we did still have to compare those manually against what's in master. Both @kristoferbaxter and @developit have been joining forces and created a bot which does that automatically. As soon as the pipeline on a PR succeeds it will add a comment listing all the size differences :+1:


Bug Fixes

  • Fix className not being applied when set to an empty string with preact/compat (#2309, thanks @JoviDeCroock)
  • Run karma tests against build output (#2300, thanks @andrewiggins) :heart:
  • Fix: Resolves Node submodule subpath error (#2304, thanks @4cm4k1)


  • Add to umd and make browser a modular export for future bundlers (#2311, thanks @JoviDeCroock)
  • Add root exports (#2310, thanks @JoviDeCroock)
  • Removes warning from tests by removing console.log in render.test.js (#2305, thanks @theZieger)

tl;dr: This release contains a good number of bug fixes and we encourage all users to upgrade.

A little bit of time has passed since our last release and we're excited to ship another one, making Preact even more robust! The fixes nearly touch all packages and further improves compatibility with third-party libraries :tada:

If you glance at the contributor names, you'll notice a few new ones there. It's safe to say that we were amazed and super ecstatic by the amount of new first time contributors to Preact! :raised_hands:


  • Include support for customised built-in elements (#2266, thanks @defx)
  • Upgrade devtools adapter to support Profiler and bring back preact/devtools import (#2246, thanks @marvinhagemeister)

Bug Fixes

  • Suspended components should rerender through shouldComponentUpdate (#2125, thanks @andrewiggins)
  • Reduce reads of dom.nextSibling (#2294, thanks @andrewiggins)
  • Preserve state when using placeholders (#2295, thanks @andrewiggins)
  • Fix not flattening result (#2287, thanks @marvinhagemeister)
  • Fix error when setting size to an invalid value (#2285, thanks @marvinhagemeister)
  • Fix wrong SVG attribute for clipPathUnits (#2251, thanks @friebe)
  • Fix both class and className being enumerable (#2280, thanks @marvinhagemeister)
  • Fix className patch not applied to props (#2279, thanks @marvinhagemeister)
  • Bailout when hook throws an error (#2193, thanks @JoviDeCroock)
  • Avoid removing existing dom nodes on subsequent replaceNode calls (#2274, thanks @JoviDeCroock)
  • Fix shouldComponentUpdate getting called on setState after forceUpdate (#2258, thanks @laino)
  • Remove process.env.NODE_ENV check from preact/debug which broke browsers (#2257, thanks @marvinhagemeister)


  • Allow null as an initial value for useRef (#2281, thanks @armujahid)


  • Rename _lastDomChildSibling to _nextDom (#2297, thanks @andrewiggins)
  • Improve code coverage (#2299, thanks @andrewiggins)
  • Update mangle.json with Suspense prop rename (#2298, thanks @andrewiggins)
  • Fix SVG polygon test failing on IE 11 (#2288, thanks @marvinhagemeister)
  • Try out new node submodule exports (#2283, thanks @JoviDeCroock)
  • Add section about reporting bugs (#2278, thanks @marvinhagemeister)
  • Improve CONTRIBUTINIG + comments (#2277, thanks @marvinhagemeister)
  • Add size action (#2270, thanks @developit)
  • Prevent postinstall from running on installation (#2271, thanks @JoviDeCroock)
  • Add test coverage for createElement (#2273, thanks @zubhav)
  • Fix custom element tests failing in IE (#2272, thanks @marvinhagemeister)
  • Fix failing IE test (#2267, thanks @marvinhagemeister)
  • Add info for first time contributors (#2265, thanks @marvinhagemeister)
  • Add unit tests to check proper component unmounting (#2195, thanks @timon-witt)
  • Add npm ci to speed up travis (#2255, thanks @JoviDeCroock)
  • Add package-lock.json (#2254, thanks @JoviDeCroock)

This release corrects an issue regarding hydration that was found in yesterdays 10.2.0 release :tada:

Bug Fixes

  • Fix duplicate text node with hydrate() (#2238, thanks @JoviDeCroock)

Happy belated New Years to everybody :tada: We hope you enjoyed the holidays and had some time off to recharge :+1: Our very first release in 2020 brings two new features and the usual round of bug fixes :100:

New useErrorBoundary hook

There is a new hook called useErrorBoundary which allows you to catch errors that are thrown by any child components. It's essentially the hook version of componentDidCatch.

// 1. parameter is null or the error that was caught
// 2. paremeter can be called to reset the state
const [err, reset] = useErrorBoundary();

// Optional: You can pass a callback function that will
//  be executed when an error occurs:
const [err] = useErrorBoundary(() => callMeMaybe());

Usage example:

// Example component that will throw an error on render
const SomeComponent = () => {
	throw new Error("fail");

const App = props => {
	const [err] = useErrorBoundary();

	if (err) {
		return <p>Something went wrong...</p>;
	} else {
		return <SomeComponent />;

Lazy works with non-default export

This PR was one of the smallest ones, but something that makes working with different kind of lazy loaded modules a lot easier. Previously lazy would always use the default export of the imported module. With this change it's now possible to use it with any export.

// Look ma, no default export
const LazyFoo = lazy(() => import("./Foo").then(m => m.MyComponent));

On top of that we have the usual round of bug fixes. We'd like to thank everyone who reported them and helped us make Preact even better. Thank you so much!! :+1:


  • Add useErrorBoundary hook (#2205, thanks @JoviDeCroock)
  • Allow lazy() usage with non-default imports (#2212, thanks @developit)

Bug Fixes

  • Fix incorrect ref value on sibling vnodes (#2217, thanks @JoviDeCroock)
  • Fix stale closure in error boundary (#2225, thanks @JoviDeCroock)
  • Fix Text nodes being re-rendered unnecessarily (#2215, thanks @developit)
  • Shorten and correct renderToString dependency error (#2207, thanks @developit)
  • Support combination of getDerivedStateFromError and componentDidCatch (#2200, thanks @JoviDeCroock)
  • Fix compat hydration (#2206, thanks @JoviDeCroock)


  • Add onReset/onFormData to Form Event types (#2209, thanks @thesmartwon)


  • More README updates (#2235, thanks @developit)
  • Modernize our README (#2232, thanks @JoviDeCroock)
  • Update package metadata (#2230, thanks @developit)
  • Add more test cases for Suspense (#2229, thanks @sventschui)
  • Fix renderer tests by using sinon global (#2220, thanks @JoviDeCroock)
  • Update JSDoc comments (#2187, thanks @soulhat)

tl;dr: A tiny maintenance release, which helps with debugging Preact apps.

This release is a lot smaller compared to our usual ones, but we deemed one feature important enough to have in users hands that we made this release. And that's component stacks which we hope will reduce the issue count in our tracker slightly :tada:

On top of that we want to congratulate @jamesb3ll for his first-time contribution to Preact :1st_place_medal: He found an issue and filled it with every detail one can imagine (codesandbox is awesome!). But instead of stopping there, he tinkered a bit and found a genius fix for it! Thank you for your PR :+1: :four_leaf_clover:

Component Stacks

Whenever you include preact/debug you get a lot of hints and warnings about how you can make your application better. But sometimes it was hard to tell where the error originated from. To resolve that we automatically append a component stack trace telling you directly which component threw the error.

class Foo extends Component {
  constructor(props) {

    // Doesn't do anything, `this.state = { foo: true }`
    // should be used instead.
    this.setState({ foo: true });

  render() {
    return <div>foo</div>;

function Bar() {
  return <Foo />;

function Baz() {
  return <Bar />;

The above code will print the following warning to the browser's console:

Screenshot from 2019-12-16 20-55-21

With the stack appended at the bottom it's much easier to track down the source :100: To get those beautiful file and line mappings, make sure that you have @babel/plugin-transform-react-jsx-source enabled in your babel config :+1:


  • Add component stack in debug warnings (#2179, thanks @marvinhagemeister)

Bug Fixes

  • Fix componentWillReceiveProps not called on child component when parent is queued in the same commit (#2186, thanks @jamesb3ll)


  • Workaround for sinon esm bundle (#2188, thanks @marvinhagemeister)

tl;dr: This release adds support for the highly anticipated preact-devtools extension. It's in an early preview state, but it has proven to be very useful already for inspecting a component tree in our internal testing. Apart from that there is a new SuspenseList component to control loading in lists and the usual round of bug fixes.

Christmas comes early in the form of another feature packed Preact release :tada: We're particular proud of this one as it represents the results of a lot of work behind the scenes. Especially when it comes to the devtools.

Developers, Developers, Developers! :wrench:

For the longest time we've been able to reuse the react devtools extension that was (as the name implies) written specifically for React. We did this by hooking ourselves into the init procedure and shimming in a conversion layer that translated our inner workings to something React would use under the hood. Over the past year we've kept up with all the internal changes of React's private structures, but it took us more and more time to make sure that the integration wasn't breaking or running into weird edge cases.

Faced with a choice we decided to pursue the development of our own extension specifically written for Preact. This way we are not affected by any breaking changes on React's side and have the possibility to extend the devtools with custom UI, like for the Composition-API PR #1923 .

That said the extension is not what we would call final yet. It's more of an early preview, akin to an alpha release. Despite bugs you may encounter, we found it useful enough in our testing that we didn't want to hold back any longer.

Download it here:


SuspenseList :1234:

SuspenseList is a new component that can control the order in which any child suspensions are revealed. Take a list of images for example. Due to the browser firing the requests to download them in parallel, the images may appear in any order. This can be a bit jarring, when some sort of appear animation is involved. With SuspenseList we can force all images to appear at the same time, inorder or in reverse.

In the following example A will appear first, followed by B even if C was loaded before B. And finally C will appear.

// `revealOrder` can be one of 'forwards', 'backwards' or 'together'
<SuspenseList revealOrder="forwards">
  <Suspense fallback={<span>Loading...</span>}>
    <A />
  <Suspense fallback={<span>Loading...</span>}>
    <B />
  <Suspense fallback={<span>Loading...</span>}>
    <C />


  • Add support for preact-devtools (#2148, thanks @marvinhagemeister)
  • SuspenseList optimisations (#2121, thanks @jviide)
  • Add SuspenseList component (#2063, thanks @prateekbh)

Bug Fixes

  • Suspense should support unmounting suspender (#2134, thanks @sventschui)
  • Add correct this type for event handlers (#2166, thanks @marvinhagemeister)
  • Prevent crash in IE when setting type attribute (#2147, thanks @Rafi993)
  • Always use lower cased touch events in compat (#2120, thanks @sventschui)
  • Fix wrong DOM order on suspend inside Fragment (#2107, thanks @jviide)


  • Add onToggle event to TypeScript defs. (#2151, thanks @xorgy)
  • Re-export FunctionComponent from preact/compat (#2087, thanks @jokester)
  • Fix internal Suspense-related typings (#2117, thanks @jviide)
  • Specify valid dir property values (#2108, thanks @antonk52)

Golf :golf: :golfing_woman:

  • Optimize createElement (#2135, thanks @developit)
  • Rename useMemo _callback to _factory (+0 B) (#2131, thanks @andrewiggins)
  • Remove redundant if clause in suspense _catchError (#2119, thanks @sventschui)
  • Consolidate VNode compat options (-62 B) (#2116, thanks @andrewiggins)


  • Disable benchmarks on CI (#2167, thanks @marvinhagemeister)
  • Fix minor spelling and grammar issue (#2165, thanks @ivanjonas)
  • Update perf tests for Preact X and enable on master (#2158, thanks @andrewiggins)
  • Rewrite touch event tests to not use internals (#2157, thanks @andrewiggins)
  • Improve test DOM helpers (#2152, thanks @andrewiggins)
  • Rewrite some tests to no longer rely on internals (#2132, thanks @andrewiggins)
  • Fix cross browser tests assertions (#2127, thanks @andrewiggins)
  • Modularize compat src and tests (#2124, thanks @andrewiggins)
  • Add test to cover hooks debug helper (#2115, thanks @andrewiggins)
  • Add test for lifecycles and state of a delayed suspending compo… (#2114, thanks @andrewiggins)

tl;dr: This release is a bug fix only release and all users are encouraged to update.

This week saw many cool improvements surrounding our TypeScript definitions. Thanks to an amazing contribution from @lukeshiru the event target is now correctly inferred for all native elements. This alone should remove many manually casted event arguments in your code :tada:

Together @JoviDeCroock and @cristianbote set their minds on fixing a few newly reported issues surrounding refs and the like. Personally, I'm pretty impressed how quickly they could identify and resolve the issues. Much respect to you two :+1:

As the year is coming to an end @andrewiggins did some house-cleaning and found various places where we could save even more bytes! I don't know how he does it and it's just amazing to witness so much pure talent!

Beside that, the changes mainly revolve around maintenance tasks. We've switched to prettier for automatic code formatting, lowering the barrier for new contributors even more. The formatting is automatically applied on each commit via a git-hook, and everything will be taken care of for you :100:

We also saw two exciting contributions from Googlers: @jridgewell found a very hard to spot unnecessary case in a regex we use to append px to certain CSS values and @jakearchibald found an html attribute we missed in our typings :tada:

Like in our past release we'd like to take a moment to thank everybody who contributed, not just code but also made the time to write bug reports. Thank you so much :+1:

Bug Fixes

  • Cleanup changing refs correctly (#2055, thanks @JoviDeCroock)
  • Ensure renderCallbacks are called when sCU bails out (#2081, thanks @JoviDeCroock)
  • Handle the stale ref for forwardRef (#2075) (#2076, thanks @cristianbote)

Golf :golfing_woman:

  • Simplify hook scheduling logic (-17 B) (#2085, thanks @andrewiggins)
  • Improve tree-shakeability (-13 B) (#2079, thanks @andrewiggins)
  • Simplify ternary expressions (-7 B) (#2049, thanks @andrewiggins)
  • Remove CSS custom property check from IS_NON_DIMENSIONAL (#2046, thanks @jridgewell)


  • Add types for currentTarget on event handlers for IntrinsicElements (#2084, thanks @lukeshiru)
  • Improve ref typings for IntrinsicElements (#2070, thanks @lukeshiru)
  • Add as html attribute to TypeScript defs (#2068, thanks @jakearchibald)


  • Add funding field to package.json (#2096, thanks @developit)
  • Upgrade tests to use Babel 7 (#2094, thanks @andrewiggins)
  • Rewrite createContext tests to no longer import internal data (#2090, thanks @andrewiggins)
  • Remove preact-charts from README (#2092, thanks @pmkroeker)
  • Update README section about preact/devtools (#2091, thanks @peterswallow)
  • In tests, import code under test by name instead of relative path (#2086, thanks @andrewiggins)
  • Replace __p to __ in mangle.json (#2044, thanks @38elements)
  • Make all .json files use 2-space (#2078, thanks @developit)
  • Adding Intergram to the demos list (#2064, thanks @idoco)
  • Improve formatting (#2071, thanks @andrewiggins)
  • Improve formatting (#2066, thanks @andrewiggins)
  • Add automatic formatting via prettier (#2065, thanks @marvinhagemeister)

This release fixes a build issue that caused errors when using effect hooks.

  • Add missing _renderCallbacks mangle configuration (#2060, thanks @robertknight)

This release corrects an issue where a debug warning was printed incorrectly to the console, that (rightfully) confused users. So we wanted to fix that as quick as possible :+1:

Bug Fixes

  • Fix setState warning always being printed (#2057, thanks @marvinhagemeister)


  • Queue layoutEffects in component render callbacks array (+0 B) (#2050, thanks @andrewiggins)
  • Separate debug tests into multiple files and fix some bugs in debug (#2047, thanks @andrewiggins)

tl;dr: This is release contains mostly bug fixes and some size reductions. We encourage everyone to upgrade.

Just a week has passed since the last release and there are already an exciting number of new commits in master. Hackoktoberfest has had really positive effects on us and we're excited to see some new contributors in this release :100:

As you can see from the changelog: @andrewiggins is currently on a byte removing spree, so I thought it'd be appropriate to list them in their own category :tada: A few others joined him and marie kondo'ed everything that didn't spark joy in us!

Besides some great savings, there are new debug warnings for components and fixes for our Suspense implementation :tada:


  • Add debug warnings when calling setState or forceUpdate on an unmounted component (#2037, thanks @andrewiggins)

Bug Fixes

  • Support re-suspending with <Suspense> (#2025, thanks @andrewiggins)
  • Add unpkg aliases (#2032, thanks @developit)
  • Fix ref not always called in useImperativeHandle (#2021, thanks @JoviDeCroock)

Golf (size reductions)

  • Inline coerceToVNode inside of toChildArray (-21 B) (#2040, thanks @andrewiggins)
  • Remove unused mount check in context Provider (-6 B) (#2039, thanks @andrewiggins)
  • Golfed render (#2045, thanks @MohammedSheikhIbrahim)
  • Remove useless return from eventProxy (#2026, thanks @jridgewell)
  • Rewrite useImperativeHandle to use useLayoutEffect (-35 B) (#2003, thanks @andrewiggins)
  • Re-purpose renderCallbacks as a general per-component commit queue (#2011, thanks @andrewiggins)


  • Add nonce to JSX HTMLAttributes types for better Content Security Policy support. (#2035, thanks @calvinf)
  • Remove internal import from compat types (#2018, thanks @andrewiggins)
  • Fix internal vnode typings (#2015, thanks @pmkroeker)


  • Remove end of line in compat/mangle.json (#2053, thanks @38elements)
  • Add _afterPaintQueued and _fallback to mangle.json (#2043, thanks @38elements)
  • Remove OpenCollective postinstall Banner (#2033, thanks @developit)
  • Update preact/compat in readme (#2029, thanks @developit)
  • Fixed some typos and grammatical mistakes. (#2028, thanks @MohammedSheikhIbrahim)
  • Add _force and _lastDomChild to mangle.json (#2023, thanks @developit)
  • Add _suspensions to mangle (#2019, thanks @JoviDeCroock)
  • Add npm script to run compat TS tests (#2017, thanks @andrewiggins)

tl;dr: This is a standard bug-fix release with no new features. We encourage everyone to upgrade

Wow, we've been overwhelmed with the reactions to our final Preact X release! We saw a huge uptick in npm downloads to 200.000 per week :tada: Another exciting announcement is that Google AMP is officially using Preact under the hood. We've receivied many more thank you notes (and even stroopwafels!) from companies using Preact. Those range from small companies to big enterprise ones, where Preact is used in a wide span of different environments.

We preactively allocated the weeks after the big launch for a period of bug-fixes only in case something slipped through. It was admittedly a more conservative decision after our long alpha and beta period, but we wanted to make sure that you all would have a butter smooth experience no matter what :stars: It's safe to say that it went really well and we've only received a couple bug reports so far :tada:

In the short amount of time that has passed since the release, we managed to squash a good portion of those! Thank you so much to everyone for reporting bugs and helping us track them down! You rock :+1: :100:

Bug Fixes

  • Resolve attributes of reused nodes that weren't present in virtual-dom (#1987, thanks @JoviDeCroock)
  • Fix stale context values (for createContext-API) (#2005, thanks @JoviDeCroock)
  • Properly flush nested setState callbacks (-6 B) (#2010, thanks @andrewiggins)
  • Import and re-export hooks explicitly to allow for webpack tree shaking (#2006, thanks @davidbailey00)
  • Removing reference to preact-context library (#2001, thanks @jackbravo)
  • Fix forceUpdate enqueued child update being skipped (#1988, thanks @marvinhagemeister)
  • Append Portal node to container instead of prepend (#1971, thanks @toraora)
  • Always pull _force flag from component (#1984, thanks @marvinhagemeister)
  • Fix typos in JSDoc (#1973, thanks @polemius)
  • Fix replaceNode not always taking effect (#1970, thanks @JoviDeCroock)


  • Remove null from VNode.type TS definition and add some TS tests (#1994, thanks @andrewiggins)
  • Add _force to internal.d.ts (#1990, thanks @38elements)
  • Improve VNode typings (#1979, thanks @andrewiggins)


  • Golf shouldComponentUpdate (#1980, thanks @JoviDeCroock)
  • Add Songsterr to Real-World Apps section (#1972, thanks @followdarko)


tl;dr: Preact X is the next major version of Preact fully packed with features like Fragments, Hooks, componentDidCatch, Test-Utils, Debug-Warnings, many compatibility fixes and so much more :tada:

It's finally happening! After months of hard work we've crossed the finish line and are over the moon with excitement to finally mark Preact X as stable. We'd like to thank everybody who tested it and submitted bug reports.

We originally planned to release a sort-of migration release as version 9 with just the breaking changes from X, but that got canned because many users reported that the upgrade process was easy enough and didn't warrant a long migration period in-between. In fact we got many reports that the upgrade could be done in under an hour, despite some of the breaking changes in X, making the need for a migration release even less desirable.

What's new?

Preact X ships with several major features and we combined them all in a single document on our site. If you're upgrading an existing Preact 8.x project, we got you covered with a detailed upgrade guide.

To give a quick summary of the new features:

  • Fragments
  • componentDidCatch
  • preact/hooks addon
  • preact/test-utils addon
  • createContext API
  • compat moved to core
  • Plethora of compatibility fixes
  • Many new warnings in preact/debug
  • Same 3 kB size as Preact 8

Again, we highly recommend checking out our new site and specifically the what's new section.

Changes since RC 4

Bug Fixes

  • Rewrite shouldComponentUpdate handling to take more edge cases into account (#1931, thanks @JoviDeCroock)
  • Fix UNSAFE_* lifecycles being overwritten in compat (#1946, thanks @marvinhagemeister)
  • Fix PureComponent rerendering when __source changes (#1950, thanks @JoviDeCroock)
  • Fix default argument in useState not applied in rare cases (#1948, thanks @JoviDeCroock)
  • Change .forceUpdate() to participate in the update queue (#1939, thanks @developit)
  • Fix vnode._children should keep their type as array when diffing (#1924, thanks @cristianbote)
  • Allow numeric CSS values to be 0 (e.g. opacity) (#1927, thanks @JoviDeCroock)
  • Fix unnecessary rerender on equal contexts (#1925, thanks @JoviDeCroock)


  • Add disableRemotePlayback to HTML Attributes (#1955, thanks @JoviDeCroock)
  • Add volume to HTML Attributes (#1938, thanks @jessicabyrne)


  • Prevent mangling of __source and __self (#1958, thanks @JoviDeCroock)
  • Add documentation on how to do releases (#1928, thanks @marvinhagemeister)

So uhm... just yesterday we've published RC2 and we've mentioned that it would be likely the last RC before going final. Turns out that fate had other plans. One seemingly minor change to our jsx constructor function lead to pretty weird and nasty bug depending on whether the code was run in use strict mode or not. @jviide wrote up a great summary on how this issue came to be.

Bug Fixes

  • Avoid creating circular children refs in createElement (#1917, thanks @JoviDeCroock)


  • Fix PureComponent not passing generic types along (#1920, thanks @amelekhin)

tl;dr: This release contains a lot of bug fixes in many areas and we encourage everyone to upgrade. We're confident that this is the last rc release before marking X as stable.

It's time for another Preact release :tada: We've spent the past weeks on make
Preact as robust as we can. Most fixes are ones you all submitted to our
tracker. This is super awesome and we wouldn't be where we are without all your

On top of countless fixes, a few minor features found their way into the
package like isValidElement that can be used to check if something is valid to
render, meaning it's a valid child that was created by h/createElement.

The test utils also received much love and we're very impressed with how quickly
@robertknight was able to land support for async callbacks and nested calls
to act :100: This is useful in cases where triggering an effect or state change
involves async steps, such as waiting for a fetch call to resolve.

Preact Devtools are coming

We think it'd be good to give a setSate of the union here about where we are
with the devtools. As many of you are aware we've always depended on the
react-devtools extension for a long time now. We were very excited to
integrate with version 4, not just because of the new featureset, but also
because the adapter and protocol is so much better than before.

We got very far, but despite of all our efforts we ran into blocker issues with
the expected order of events that need to be sent to the extension. Our
internals differ quite a bit and after trying for weeks to get it right we
ultimately went back to the drawing board.

It became clear that just writing our own extension would be less resource
intensive and would make it easier for us to maintain. It's pre-alpha right now,
but we expect to mature in the coming weeks/month. Here is a screenshot as a
little sneak peek:


Very early pre-alpha preview of preact-devtools

We'll keep you posted! Here is the full list of changes in this release:


  • Add isValidElement to core (#1861, thanks @marvinhagemeister)
  • Throw error when passing plain objects as children (#1858, thanks @marvinhagemeister)
  • Add support for form attribute on buttons and inputs (#1863, thanks @sventschui)
  • Warn when calling setState in a constructor (#1857, thanks @marvinhagemeister)
  • Implement support for async act callbacks and nested calls to act (#1854, thanks @robertknight)

Bug Fixes

  • Fix nested setState calls with accurate this.state (#1748, thanks @JoviDeCroock)
  • Fix useImperativeHandle handles after rendering (#1909, thanks @JoviDeCroock)
  • Fix shouldComponentUpdate leading to invalid DOM without children (#1888, thanks @JoviDeCroock)
  • Add shouldComponentUpdate check for context.Consumer (#1901, thanks @cristianbote)
  • Fix replaceNode argument not always replacing node (#1900, thanks @JoviDeCroock)
  • Fix incorrect dom pointer with shouldComponentUpdate (#1871, thanks @marvinhagemeister)
  • Fix IE11 regressions (#1856, thanks @marvinhagemeister)
  • Fix undefined styles not being cleared (#1853, thanks @marvinhagemeister)
  • Fix act not flushing effects/updates if global effect/update queues are non-empty before act call (#1851, thanks @robertknight)
  • Support adding refs to memo'ed components (#1860, thanks @sventschui)
  • Tag forwardRef & memo proxy components as React components (#1844, thanks @developit)
  • Fix IE11 not setting initial select value (#1838, thanks @marvinhagemeister)
  • Avoid accessing fn.prototype where possible (#1835, thanks @developit)
  • Only destroy DOM on first render (#1832, thanks @marvinhagemeister)
  • Flatten children array as it is diffed (#1716, thanks @andrewiggins)
  • Optimize creating array of childrens (#1743, thanks @Connormiha)


  • Fix compat types (#1752, thanks @marvinhagemeister)
  • Add key to all jsx elements (#1887, thanks @marvinhagemeister)
  • Fix type definitions for getDerivedStateFromProps and getDerivedStateFromError (#1874, thanks @38elements)
  • Add VNode.constructor to internal type definitions (#1810, thanks @38elements)


  • Add info message about react-devtools v4 (#1908, thanks @marvinhagemeister)
  • Add back Code of Conduct from Preact 8 branch (#1897, thanks @marvinhagemeister)
  • Make source files non-executable (#1894, thanks @jridgewell)
  • Skip form attribute test on IE11 (#1868, thanks @marvinhagemeister)
  • Re-enable coveralls (#1867, thanks @marvinhagemeister)
  • Disable coveralls until their outage is resolved (#1866, thanks @marvinhagemeister)
  • Delete .flowconfig (#1855, thanks @38elements)
  • Remove Flow type definition (#1846, thanks @38elements)
  • Remove a duplicate test in render tests (and add additional child node checks) (#1847, thanks @OrKoN)
  • Exclude Edge from running devtool tests (#1839, thanks @marvinhagemeister)
  • Remove double build for TravisCI (#1837, thanks @cristianbote)
  • Exclude devtools tests from coverage (#1808, thanks @marvinhagemeister)
  • Fix test in IE by sorting attributes. (#1836, thanks @marvinhagemeister)

tl;dr: This release contains many little fixes and we encourage all X users to upgrade.

As we're nearing closer to the actual release we're hoping to sweeten the wait with another rc-release :tada: While the docs are coming along nicely we have a preview of our upcoming migration guide to Preact X from 8.x. We have a little polish left to do, but apart from that the new docs are basically ready :fingers_crossed:

But back to the actual release! This time the fixes touch a broad area in Preact. There are a few for Portals, some for onTransition* or onAnimation* events and the usual round of browser or typing PRs.

It's arguably less feature packed than previous releases, but that's a really good sign for the release candidate phase. It means that X is mature and is in great shape for going gold :+1: :100:


  • Diff props when replacing a node and don't diff when hydrating (#1786, thanks @JoviDeCroock)

Bug Fixes

  • Fix lazy not forwarding refs (#1826, thanks @JoviDeCroock)
  • Fix Chrome Headless running on WSL (#1696, thanks @developit)
  • Use internal reference for hydration flag (#1802, thanks @developit)
  • Fix animation and transition events handlers not being recognized (#1804, thanks @JoviDeCroock)
  • Fix IE11 incompatability with input type="range" (#1823, thanks @JoviDeCroock)
  • Add additional test for createPortal -> normal vnode (#1805, thanks @JoviDeCroock)
  • Fix Portal children always being mounted (#1781, thanks @marvinhagemeister)
  • Fix debug crash with hooks import (#1773, thanks @JoviDeCroock)


  • Add Partial to setState (#1779, thanks @pmkroeker)


  • Fix typos across preact repository (#1796, thanks @kittenking)
  • Fix typo (#1784, thanks @38elements)
  • Upgrade xvfb config for Travis CI (#1822, thanks @prateekbh)
  • Add Web Maker to real world app list (#1762, thanks @chinchang)

tl;dr: This is mostly a bugfix only release, with one notable exception. Due to popular demand we reversed our decision to remove support for string styles and brought them back :four_leaf_clover: All users on the next tag are encouraged to upgrade :+1:

You may have already noticed the rc suffix in the release number and that is because we're finally out of beta :+1: rc stands for roughly complete... uh... release candidate and this means that a final Preact X release is really close :checkered_flag: We do wanted to give it another phase of testing before going final though and it gives us a bit time to get our docs in shape.

8.x will be maintained further :wrench:

Don't worry if there is something you would have loved to see in X, but that didn't make the cut. The final X release is more of a sign that we think it's stable and maybe even more so than 8.x ever was :tada: This also means that we can focus more on adding new features and reduce our current maintenance overhead of working on two release lines simultaneously. The upgrade should be pretty seamless for everyone and we'll publish a complete migration guide along with the final X release. If you for some reason can't update to X don't worry. We'll keep maintaining the 8.x release line for a good while.

Now You’re Thinking With Portals

This release contains some noteworthy fixes to our Portal-Component. But not just that, we also added back support for string styles due to popular demand. Originally we removed them to save bytes, but it turns out that this feature is way to useful not to have. Additionally it aligns better with out philosophy with being "closest to the DOM". Thank you'all for everyone who participated in that journey and helped us make the right decision moving forward :+1: :100:

SSR Boost :speedboat:

This change has been cooking for a while and plays well along with the research @developit and @housseindjirdeh have been doing about hydration. When you look at the way SSR works, you'll notice that pretty much the complete diff phase is redundant. The HTML that is sent to the client matches the vnode tree completely, so there isn't much point in comparing changes. The thing is that we can't remove it completely though, because we still need to attach event listeners. For that case we introduced a faster code path which bypasses most of the diffing. After the initial render is done, Preact will continue with the usual diff mode.

Note: This may be a breaking change if you are sending an incorrect DOM structure to the client and relied on hydrate to correct that for you. To fix this make sure to sent HTML that matches the initial vnode tree on the client.

Rock solid ecosystem

At this point we'd like to give a shoutout to @robertknight who continues to quietly maintain enzyme-adapter-preact-pure. It's rock solid already and it's only getting better. Be sure to check it out :nerd_face:

Without further ado: Here is the full changelog of our first (p)release candidate :1st_place_medal:


  • Add back support for string styles (#1744, thanks @marvinhagemeister)

Bug Fixes

  • Add requestAnimationFrame fallback when tab is not focused (#1763, thanks @calebeby)
  • Move Code of Conduct into .github dir (#1765, thanks @developit)
  • Move eslintignore into package.json (#1764, thanks @developit)
  • Fix various scenarios with Portals (#1749, thanks @JoviDeCroock)
  • Minor debug enhancements (#1755, thanks @marvinhagemeister)
  • Bypass props during hydration (#1697, thanks @developit)
  • Fix uncontrolled inputs not changing value (#1760, thanks @JoviDeCroock)
  • Replace catchRender with catchError (#1742, thanks @andrewiggins)
  • Warn on invalid table DOM structure (#1745, thanks @JoviDeCroock)
  • Fix replaceNode not unmounting when diffing twice with replaceNodea (#1723, thanks @JoviDeCroock)
  • Fix up compat types and tests (#1740, thanks @andrewiggins)
  • Golf sibling DOM handling (#1737, thanks @andrewiggins)
  • Add return type to _childDidSuspend (#1735, thanks @pmkroeker)
  • Fix compat render not destroying existing DOM (#1729, thanks @marvinhagemeister)

tl;dr This is a bug-fix-only release and all users are encouraged to update. A final release is in sight :tada:

It's the summer and while hopefully most of you will be able to enjoy the time
outside we couldn't pass the opportunity to let our latest beta release free
into the world! This might be the last beta we'll publish before going gold,
so stay tuned :wink: For this reason we focused purely on shaking all bugs out
and make this the most stable preact release we can :+1: :100:

Most notably this includes some rethinking of how we deal with Fragments
internally. Over the past weeks we received bug reports which all seemed to be
related and it turns out they were! They all shared the problem that the sibling
order was different after a component update. We all got our heads together
and found a better internal implementation for Fragments that's a lot more
sound theoretically. It should fix all ordering issues for good :+1: :checkered_flag:

Although this release is quite and we've set our sights on a final release we're
already working on getting the Preact debug adapter in shape for the upcoming
react-devtools v4 :tada: It's not just more performant, but also adds the
hooks panel right in the devtools. More on that soon :wink:

Again, you are awesome

As always Preact wouldn't be what it is today without you all. Whether you've
been helping us squash bugs, joined feature discussions or just spread the word,
we're really thankful to be part of such an amazing community. We even got to
meet some of you at this years JSConfEU!

With all said: We can't wait to unleash the final release soon! If you're new
to the beta releases, don't worry. The final release will be accompanied with
a migration guide :+1:


Because of the internal restructuring preact-render-to-string needs to be updated to at least version 5.0.4.

Bug Fixes

  • Restore debounceRendering after act (#1683, thanks @JoviDeCroock)
  • Fix cross browser assertions (#1708, thanks @marvinhagemeister)
  • Bubble up Component dom changes up the virtual tree (+51 B) (#1700, thanks @andrewiggins)
  • Begin rendering with diff instead of diffChildren and other golf ⛳ (#1715, thanks @andrewiggins)
  • Implement useRef with useState (#1679, thanks @JoviDeCroock)
  • Fix falsy event values beeing added (#1712, thanks @cristianbote)
  • Minify names of externally exposed private functions (#1711, thanks @andrewiggins)
  • Ensure correct unmount (#1687, thanks @JoviDeCroock)
  • Correct Portal unmounting and props diffing (#1691, thanks @JoviDeCroock)
  • RFC: Privatize some options (-7 B) (#1692, thanks @andrewiggins)
  • Begin diff with next DOM sibling in forceUpdate (#1689, thanks @andrewiggins)
  • Replace ancestorComponent with vnode parent pointer (+5 B) (#1688, thanks @andrewiggins)
  • Delete React debugging props __self and __source (#1690, thanks @mxstbr)
  • Consistently use _children & combine Fragment and Component diffing (-73 B) (#1658, thanks @andrewiggins)
  • Don't use typeof check for h (#1676, thanks @JoviDeCroock)


  • Update compat typings (#1703, thanks @pmkroeker)
  • Retype style to error on strings (#1675, thanks @pmkroeker)


  • Update suspense so tests only rely on public API (#1724, thanks @andrewiggins)
  • Disable Safari in Saucelabs tests (#1706, thanks @marvinhagemeister)
  • Add redux to demo app (#1705, thanks @marvinhagemeister)
  • Clean up scratch DOM after tests (#1701, thanks @andrewiggins)
  • Separate lifecycle tests into separate files (#1702, thanks @andrewiggins)
  • Add toEqualNode chai assertion (#1680, thanks @andrewiggins)
  • Run ESLint on all source and test files (#1686, thanks @andrewiggins)
  • Add preact-charts to Component Libraries (#1667, thanks @pmkroeker)

tl;dr: We're excited to be one step closer to a final Preact X release! It's packed full with the addition of Suspense, lazy, performance improvements and the usual round of bug fixes :tada:

Internally we've been referring to this one as the "Hitchcock"-Release. @jviide came up again with the very fitting release title :+1: We're proud to welcome @sventschui as the newest addition to our team! And oh boy did he join us with a bang! :tada:

The Suspense Is Real

@sventschui went ahead and added basic support for both the Suspense component and lazy() to preact/compat. With these in place it is now a lot easier to do proper code-splitting. Just have a look at this example on how they can be used together:

import { Suspense, lazy } from "preact/compat";

const Other = lazy(() => import('./OtherComponent'));

function Foo() {
  return <Suspense fallback={<div>Loading...</div>}>
    <Other />

In the snippet above the Other component will only be displayed once it's loaded. Until then the Suspense component allows the user to display any fallback content making it ideal for any sort of spinners for example :+1: :1st_place_medal:

This was one of the remaining road blocks in getting Next.js to work with Preact X :tada: Note that our implementation is still considered an experimental preview :clock1030:

The golfing continues :golf: :golfing_man:

Last cycle we had a lot of fun finding ways to make Preact even smaller! In fact, we had so much fun that we continued a bit to do so leading up to this release :100: These size optimizations are really important, because they allow us to offset the byte cost for new features while staying within our self imposed byte size limit :whale:

Faster hydration :zap: :checkered_flag:

Following @developit and @housseindjirdeh's talk on Progressive Hydration at Google I/O'19, we've landed the first changes to support a more ideal way of handling hydration. Note that this is formally a breaking change, but it won't likely cause you any trouble. What's different is that existing server-rendered DOM element attributes will no longer be removed during hydration. This translates to a huge performance benefit for boot-up time, which means your page gets interactive and ready-to-go noticeably sooner if you are doing Server Side Rendering or prerendering :rocket:

Removed string styles

We removed support for string styles in favor of setting style properties only with objects. This aligns our behavior with various other virtual-dom based libraries out there :heavy_check_mark: Note that this is a breaking change from 8.x and you may need to update your code:

// before
<div style="color: LightGoldenrodYellow;" />

// after
<div style={{ color: "LightGoldenrodYellow" }} />

This one was long on our list of possible ways to cut down on size. Nonetheless we are aware that this might be a controversial change. If you have a lot of string styles or want to continue using them, you can use preact-string-styles to patch this behavior back into Preact X. If you don't agree this change, please let us know!

Preact Sightings :atom_symbol:

Sometimes we need a bit inspiration on how to build complete Applications with Preact. We were super stoked to see that one of the demos at Google I/O uses our library and can be inspected here on GitHub. The demo is a minesweeper-like game called Proxx that makes clever use of some advanced web features like Web Workers and WASM. Be sure to check it out!

If any of you wonderful people happen to be at JSConfEU in Berlin this weekend, let's meet in person! Both @cristianbote and I (@marvinhagemeister) will be attending, and we have plenty of stickers with us :wink:

In closing we've been super thankful for every PR or bug report we received in the past cycle. They are crucial to make Preact rock solid and help us find edge cases we may not have thought of. Thank you all!



  • Introduce Suspense and lazy (#1593, #1638 thanks @sventschui)
  • Don't read attributes during hydration (#1596, thanks @developit)
  • Update README with the proxx demo (#1654, thanks @talentedandrew)
  • Make consistent use of key check logic (-12 B) (#1651, thanks @andrewiggins)
  • Update links for the new preactjs org (#1649, thanks @JoviDeCroock)
  • Support JSX-sourcemaps (#1628, thanks @JoviDeCroock)
  • Simplify key matching technique (#1637, thanks @developit)
  • Remove the unused bundlesize dependency (#1641, thanks @JoviDeCroock)
  • Add travis-size-report to the CI (#1633, thanks @wardpeet)
  • Revert the lookup on documentView since this introduced a regression (#1631, thanks @cristianbote)
  • Show useEffect warning instead of throwing error (#1623, thanks @yuqianma)
  • breaking: Remove support for style as string and other golfing (-32B) (#1606, thanks @cristianbote)
  • Golf Hooks (-11B) and compat (-18B) (#1602, thanks @JoviDeCroock)
  • Code golf preact.js.gz size down by 53 B ⛳️ (#1599, thanks @jviide)
  • Store text node's text content as VNode.props instead of VNode.text (#1600, thanks @jviide)

Bug fixes

  • Update Fragment DOM log with null placeholder behavior (#1661, thanks @andrewiggins)
  • Prevent crash with nullish ref (#1657, thanks @JoviDeCroock)
  • Remove props.key even when key === 0 or key === "" (#1607, thanks @jviide)
  • Fix unmount behavior when using replaceNode (#1647, thanks @JoviDeCroock)
  • Fix react-hot-loader compatibility by not checking on vnode ref (#1644, thanks @JoviDeCroock)
  • Fix this reference in componentWillUnmount under context Consumer (#1627, thanks @sventschui)
  • Ignore dangerouslySetInnerHTML during hydration (#1595, thanks @developit)
  • Don't compare vnodes when oldVNode has no dom reference (#1617, thanks @JoviDeCroock)
  • self is not available in non-browser environments (#1618, thanks @cristianbote)
  • .some should not be used for effect invoke/cleanup (#1613, thanks @JoviDeCroock)
  • Only warn once about argumentless effects (#1625, thanks @JoviDeCroock)
  • Warn about missing preact-render-to-string dependency (#1603, thanks @JoviDeCroock)
  • Insert portal children instead of overwriting the container (#1629, thanks @JoviDeCroock)


  • Add types to compat (#1609, thanks @pmkroeker)
  • Add types for Suspense/lazy (#1619, thanks @pmkroeker)

tl;dr: This release contains important bug fixes to our reconciler and a security fix.
We highly recommend to upgrade to this version if you are running a past alpha
or beta release of Preact X.

Code-Golf to the max :golf: :golfing_woman:

As mentioned in our past release notes we spent more time on making preact even
smaller. It didn't take long until @andrewiggins found ways to cut down close
to 40 bytes in just a few hours. We don't know how he does it, but the bytes
are surely afraid of him by now! Amazed by that headstart @jviide soon followed
with a PR of his own which removed another 30+ bytes. He's been helping us
with loads of performance improvements in the past weeks and you may already
know him from his outstanding contributions to htm,
but this is his very first PR to Preact. Be sure to give him a high-five when
you see him somewhere :tada:

Lazor sharp focus :telescope:

The temptation was real and @JoviDeCroock tackled one of the bigger fish that
was back on our minds for a while: Our focus workaround. Due to past bugs in
our reconciler during the alpha phase we had to add a workaround so that a
focused input wouldn't lose focus when sibling elements were moved or removed
completely. The workaround took some toll on the final build size and jovi
managed to finally crack the nut and made it possible to remove that workaround.

After just a few days we're happy to report that close to 100 bytes had to pack
up their things and leave the repo :point_right: We're super happy with the result and have
surpassed our initial approximations on possible savings :checkered_flag:

VNode hardening :muscle:

One thing that's very important for us is making sure that we make it easy to
do the right thing by default regarding security. For that all modern frameworks
prevent the most common Cross-Site-Scripting Attacks (short: XSS). Frameworks
that are based on the virtual-dom are unique in that regard as most exploits
rely on some way to inject a malicious string through innerHTML.

Preact 8.x included a protection for the following scenario:

// Malicious JSON received from server or created
// from user input via JSON.parse() in app code
const evil = {
  type: "div",
  props: {
    dangerouslySetInnerHTML: "<script>alert('xss')</script>"

function App() {
  // Inject our evil vnode
  return <div>My name is {evil}</div>;

render(<App />, document.body);

The exploit basically comes down to a possibility of a user being able
to inject any js object as a virtual-dom node (= vnode) and in effect
being able to call dom.innerHTML = evilString. Note that this
requires either a compromised or insecure server or an input field which
is parsed via JSON.parse() and put back into JSX unmodified to pull of
the exploit successfully. This is why we think it's a low priority security

We take any security issue seriously and have added a similar protection
mechanism to Preact X :+1: Thanks goes to @JoviDeCroock and @wardpeet who did
the inital prototype.

Say hello to useDebugValue and useImperativeHandle :wave:

These two hooks are lesser known than useState and the like, but are sometimes
used in third-party libraries. useDebugValue is especially important to
remain compatible with the upcoming mobx-react which relies a lot on hooks
internally. With these 2 hooks added we are now 100% compatible with the react
ecosystem on that front :tada:

Easier hydration :heavy_check_mark:

As we all know server-side-rendering (short: ssr) is a neat way to improve
the time to first paint. This is especially important if you're working for
a newspaper or magazine where the bottom line is very dependent on performance.
First-Time contributor @LukasBombach championed an improved way to directly
replace a DOM node with a render() call. This is very useful on a site where
sections are rendered independently from each other.

It works by being able to pass a third replaceNode parameter to render.
We were not only impressed by the attention to detail in his PR, but also by
the incredible description and info-graphics around the changes. This was
beyond stunningly awesome :+1: Check out the description in his PR for more information.

Loads of little fixes :wrench:

As usual this release is packed with a bunch of fixes and minor performance
improvements. Most notably are correctness fixes and improvements to animation
event handling. On top of that we added an example page with styled-components
to test with. We knew some users had issues with it and Preact 8.x in the past
so we wanted to make sure that it will work right out of the box with Preact X.
And the good news is that we haven't found any issues so far :tada:

In general we fell like we're reaching a stage with Preact X where it is pretty
stable and mature at this point. A final release is likely not too far off in
the future :100: :wink:

But enough talk, here is the full list of changes:

  • Reduce size by golfing down some pieces 🏌️‍♂️ (-39 B) (#1578, thanks @andrewiggins)
  • Remove focusElement (#1548, thanks @JoviDeCroock)
  • Code golf diffChildren size down by 33 B ⛳️ (#1580, thanks @jviide)
  • Prevent json injection (#1528, thanks @JoviDeCroock)
  • Add replaceNode parameter to render() (#1557, thanks @LukasBombach)
  • Add support for global event handlers like animation and transition (#1590, thanks @cristianbote)
  • Add styled-components example (#1574, thanks @marvinhagemeister)
  • Add useImperativeHandle (#1556, thanks @JoviDeCroock)
  • Add useDebugValue hook (#1554, thanks @marvinhagemeister)
  • Warn about hooks outside render and/or components (#1509, thanks @JoviDeCroock)
  • Improve nested effect rerenders in act (-49 B) (#1546, thanks @andrewiggins)
  • Fix _lastDomChild disregarding placeholders (#1587, thanks @marvinhagemeister)
  • Fix useState can potentially add to the state queue and make the _dirty call neglect this (#1584, thanks @JoviDeCroock)
  • Fix infinite loop because of props mutation (#1577, thanks @marvinhagemeister)
  • Fix Error boundary not applying in array cases (#1572, thanks @JoviDeCroock)
  • Fix prevent crash when there is no last DOM child node (#1568, thanks @JoviDeCroock)
  • Fix input value not in specified range (#1435, thanks @marvinhagemeister)
  • Fix hoisting of components without DOM (#1559, thanks @marvinhagemeister)
  • Fix wrong state references (#1465, thanks @mochiya98)
  • Disable travis' email notifications (#1560, thanks @marvinhagemeister)
  • Slight improvements to code coverage (#1558, thanks @marvinhagemeister)
  • Run tests on MacOS 10.13 + Safari 11 (#1555, thanks @developit)


  • Update type definition (#1581, thanks @38elements)
  • Ts fixes (#1565, thanks @ForsakenHarmony)
  • feat: adds microdata attrs types (#1585, thanks @Dangoo)

tl;dr: This release contains several important improvements to our reconciler (the part that is responsible for moving the DOM nodes around). Because of that we highly recommended everyone who is on one of the previous alpha releases to upgrade.

Easter is coming up, and we have enough changes in master to warrant a release :tada: During the past weeks we managed to resolve lots of bugs. Not just your standard bugs, but loads of the trickier ones :+1: For the first time in a long while we are below the 100 issue mark :tada: With release we feel confident that it can be used in a wider target audience. Our featureset is complete, and we even added a lot more than was initially planned for Preact X. If you're missing something where you feel like that it must absolutely be added in the initial release and can't wait for the next minor one, please voice your opinion in our issue tracker.

Fragments considered stable

The most notable change is a very welcomed refactoring of our reconciler to fix known issues we had with Fragments. @andrewiggins spent a lot of time the last weeks on getting this one right. What's most impressive about this is that he didn't just fix one or two of the issues surrounding Fragments, and instead blew us all away by squashing all of them in one go.

In classic Andre fashion the PR is very thorough with detailed annotations about the changes in each commit. He just has that incredible talent for stepping back and assessing the situation with fresh eyes.

Demo inspired by

Next-level conditional rendering

One cool optimization that our new code base allowed us to do is introducing the concept of placeholders (often referred to as "holes") in our renderer. These placeholders allow us to skip a bit of work when updating conditionally rendered elements like in the following example:

  {condition && <Foo />}
  {!condition && <Bar />}
  <p>vroooooom 🚀</p>

Seeing that this is quite a common pattern in Preact-based apps, we are very excited about the performance improvements of that technique. Another positive side effect is that it opened up the chance to land further enhancements to keyed and unkeyed children!


Depth-based component updates

Once thing that was bugging us, is that even with version X, Preact would do more work than necessary when nested updates are involved. This happened when a child component enqueued an update via setState and later one of the parent components does the same thing, we'd render the child component twice (Child -> Parent -> Child). This happened because we would process the queue in the order the updates where added. We can effectively remove a whole render by making our processing depth-aware. Our new code detects that we can optimize the ordering by rendering the parent first which will automatically update the child component.

This work was initially started by @JoviDeCroock and later joined by the whole team. We're quite happy with the result and noticed the enhanced rendering speed in our own apps :100:

Official adapter for enzyme

For a while now @robertknight has devoted his time to greatly enhance our testing story. He wrote an adapter for the popular enzyme testing library from Airbnb. It even works with both Preact 8.x and Preact X. We've been using it quite a bit in our own projects and wanted to mark it as an official package.

At some point in the future we're planning to move all the other libs over to the organization, too. But there is no concrete timeline available for now. The main benefit for us the org provides is a shared permission model for contributions so that we don't have to bug @developit each time we make a PR to our offspring libraries like preact-router and preact-render-to-string.

Status: Bundle size

With this release we have surpassed the total bundle size of the 8.x line. Due to improved tree shaking the hello world example will be smaller though. Depending on how the future will unfold we may put more focus in the next weeks on finding ways to cut down on size :crossed_fingers: This is a lot easier to do with an extensive test suite like we have now :+1: Stay tuned!

Without further ado, here is the full changelog:

Bug Fixes

  • Keep track of childDom between diffChildren calls (#1515, thanks @andrewiggins)
  • Remove getFirstOldDom (-42 B) (#1531, thanks @andrewiggins)
  • Add support for arrays with holes as placeholders (#1440, thanks @marvinhagemeister)
  • Apply state updates based on depth (#1534, thanks @JoviDeCroock)
  • Fix incorrect depth ordering (#1536, thanks @marvinhagemeister)
  • Fix nodes with different keys being reused (#1532, thanks @marvinhagemeister)
  • Fix stale DOM with async preact-router (#1539, thanks @marvinhagemeister)
  • Fix Portals not being unmounted (#1537, thanks @marvinhagemeister)
  • act should be able to track and flush the full queue and all subsequent queues (#1520, thanks @JoviDeCroock)
  • Fix error with component attributes that start with on (#1530, thanks @JoviDeCroock)
  • Remove prop-types dependency (#1525, thanks @natevw)
  • Add clear button to demo logger (#1517, thanks @marvinhagemeister)
  • Add _depth to mangle.json (#1542, thanks @38elements)
  • Remove unnecessary code (#1543, thanks @38elements)


  • Use same type naming as @types/react (#1519, thanks @marvinhagemeister)

A bit quicker than usual, but we felt like the recent changes are worthy enough to be part of a dedicated release. Originally we planned to just include a fix for our prop-types validation in debug and when looking at our open PRs we couldn't resist to add those that were already done.

The awesome release name is brought to you by @jviide :sunglasses:

Ghosting DOM elements :ghost:

When setState/forceUpdate was called in a lifecycle other than componentDidUpdate we sometimes didn't clean up the previous DOM correctly. This lead to ghosting effects and was the last fix needed to get our new people demo fully working (it uses mobx and mobx-state-tree). We're happy to see this is resolved :+1:

JSX Typings 💯

One of the PRs we are really excited about was made by @just-boris: He moved our JSX types under a dedicated preact namespaces allowing our types and @types/react to coexists without any conflicts. This is big for anyone using TS and we can't thank him enough for his PR :tada:

Streamlined useEffect 🔢

We reworked our cleanup code for effects with this release. Before new effects are executed we cleanup all previous ones instead of cleaning and executing them one-by-one. This was confusing and the new behavior is much more obvious when writing effectful code.


As with our last releases we received numerous amazing reproductions and issues which allowed us to quickly spot a bug and fix it. We really appreciate the short feedback loop. Without it we wouldn't be able to improve Preact X as much as we can :+1:


  • warn about deprecated properties (#1511, thanks @JoviDeCroock)

Bug fixes:

  • effect execution and cleanup order (#1501, thanks @JoviDeCroock)
  • _dom being null whenever sCU returns false (#1484, thanks @marvinhagemeister)
  • debug message should not throw for undefined and null (#1505, thanks @JoviDeCroock)
  • Remove unused component import (#1508, thanks @marvinhagemeister)


  • move JSX namespace into preact one (#1448, thanks @just-boris)

It's time for another Preact X release :tada: The past weeks were very productive and we're very happy to have been able to improve Preact on many fronts.

mjs is no more :100: ✅

We are very happy to have removed our .mjs bundles and are back with standard .js files. This was a common error when trying out the alpha with webpack because the resolution for mjs files is different. We're very thankful for @lukeed who stepped up and created a workaround with a custom webpack plugin that we have been referring to. But we all knew that we wanted to correct that with our next release and make Preact work out of the box with webpack again. This release does just that and you can safely remove webpack-modules :+1:

Developer Experience Enhancements :hammer_and_wrench:

For this release we spent a few days working on improving the developer experience when writing Preact apps. For that we added more warnings to preact/debug which should catch some common issues you may have run into. These will be visible in your browsers console :tada:

  • Warn on double encoding of JSX literals and other invalid nodes passed to createElement/h
  • Warn when an event handler is not a function
  • Warn when the container node passed to render() doesn't exist in the DOM
  • Warn on invalid dependencies passed to useEffect/useLayoutEffect

Here is an example:


Other notable changes

Thanks to the stunningly beautiful "people demo" that @phaux contributed we were able to catch two issues when using Preact together with mobx-react. Our compat shim is now on a level where mobx-preact isn't needed anymore (it was lagging behind in terms of features compared to mobx-react).


What's next

We brought in the first batches of reconciler fixes but have a few pending changes left to do. These mainly include fixes that will make Fragment support rock solid and an enhancements to createContext. When these are stable we'll move forward to a beta release :+1: We even have some cool performance improvements in the pipeline 🤫

You all are amazing!

As always we'd like to take time to thank everyone who has filed issues or participated in discussion. The issues in particular are much higher quality than just a few months ago. We've received quite a few comments on twitter or in person from friends working on other OSS projects that they love our community and the positivity ours is known for. Just wanted to pass on the praise to you all :tada: :heart:

Without further ado, here is the full change set:


  • Add warnings for invalid useEffect/useLayoutEffect dependencies (#1495, thanks @JoviDeCroock)
  • Add warnings for useMemo/useCallback when dependencies haven't been specified (#1499, thanks @JoviDeCroock)
  • Add more createElement warnings in debug (#1494, thanks @marvinhagemeister)
  • Add warnings for render() (#1487, thanks @JoviDeCroock)
  • Add warning for invalid event handlers (#1409, thanks @JoviDeCroock)
  • Add teardown to test-utils (#1458, thanks @JoviDeCroock)
  • Add people demo from #1388 (#1453, thanks @andrewiggins @phaux)
  • Add back unstable_batchedUpdates and replace mobx-preact with mobx-react in demo app (#1477, thanks @marvinhagemeister)
  • Add support for CSS Grid (#1407, thanks @JoviDeCroock)
  • Switch from *.mjs -> *.module.js (#1425, thanks @marvinhagemeister)
  • Save 3 bytes (#1466, thanks @38elements)
  • Add teardown to test-utils (#1458, thanks @JoviDeCroock)

Bug Fixes

  • event.persist should be a function, not an object (#1498, thanks @JoviDeCroock)
  • Fix Portal crashing devtools (#1491, thanks @marvinhagemeister)
  • Fix stale DOM caused by empty Fragments (#1489, thanks @marvinhagemeister)
  • Add test for fragment ordering (#1478, thanks @JoviDeCroock)
  • Fix DOM removed before componentWillUnmount (#1471, thanks @marvinhagemeister)
  • Don't call setState callback or enqueue a render in constructor (#1454, thanks @JoviDeCroock)
  • Jump to the next childDom if its _dom is null (#1452, thanks @Almo7aya)
  • Apply compat normalisation for every vnode (#1450, thanks @JoviDeCroock)
  • Fix changes in getDerivedStateFromProps is not reflected to _nextState (#1446, thanks @mochiya98)
  • Fix missing mangle config for _prevState (#1442, thanks @mochiya98)
  • Always diff checked/value properties against the DOM. Fixes #1324. (#1438, thanks @utkarshkukreti) 🎉
  • Add missing test-utils export in preact/compat (#1436, thanks @JoviDeCroock)
  • Replace Object.assign with custom assign function (#1433, thanks @marvinhagemeister)
  • Don't iterate over old CSS string (#1429, thanks @marvinhagemeister)
  • Fix safari failing on calc with CSS Custom Properties in SauceLabs (#1428, thanks @marvinhagemeister)
  • Don't append px suffix for CSS Custom Properties (#1426, thanks @marvinhagemeister)
  • Updated links to the funcy.js source. (#1420, thanks @michael-klein)
  • Fix componentDidUpdate arguments (#1455, thanks @38elements)
  • Fix tagName prop trying to change element type (#1419, thanks @JoviDeCroock)
  • Fix missing className normalization on hydrate (#1421, thanks @marvinhagemeister)
  • Fix inconsistencies in act (#1437, thanks @JoviDeCroock)
  • Always diff checked/value properties against the DOM. (#1438, thanks @utkarshkukreti)
  • Render bailout on equal state transition (#1449, thanks @JoviDeCroock)
  • support svg attributes (#1451, thanks @JoviDeCroock)
  • Move context update logic to diffing (#1468, thanks @JoviDeCroock)
  • Fix remove button adding todos in demo app (#1488, thanks @marvinhagemeister)


  • Update option types (#1493, thanks @JoviDeCroock)
  • Update internal compat types (#1483, #1456, thanks @38elements)
  • Update internal context types (#1432, thanks @38elements)
  • Update Component in internal.d.ts (#1443, thanks @38elements)
  • Fix vnode type definition (#1447, thanks @38elements)
  • Remove unnecessary settings in tsconfig.json (#1457, thanks @38elements)

The weekend is coming and we thought it be a good time to make another alpha release for everyone to play with 🎉

This was a wonderful week for us. More and more bug reports contain links to a reproducible test case. You all are simply amazing! We wouldn't be able to fix this many bugs without all your help 👍 Even our contributions had an alltime high with excellent PRs from the community!

Despite fixing many bugs we landed a new package to help you test hooks in Preact 🎉

Introducing preact/test-utils

With hooks having their own scheduling logic, we didn't have a good testing story up until now! First-time contributor @JoviDeCroock went right into it and quickly had a prototype running. With the blink of an eye he had a PR that was ready to be merged. These testing utilities are inspired by the excellent react-dom/test-utils tools and share a similar act() function :+1:

For testing hooks effectively we need to flush pending effects synchronously. The brand new act() function allows you to do just that:

import { act } from "preact/test-utils";

let spy = sinon.spy();
function StateContainer() {
  return <div />;

// Wrap the render() with `act` to flush hooks automatically
act(() => render(<StateContainer />, document.body));

// Do your assertions 🎉

If you're working with class-based components you can use the new setupRerender function to flush pending state updates in your tests:

import { setupRerender, Component } from "preact/test-utils";

// Setup rerender logic first
const rerender = setupRerender();

let updateState;
class App extends Component {
  constructor() {
    this.state = { count: 0 };
    updateState = () => this.setState(prev => ({ count: ++prev.count }));

  render() {
    return <div>count: {this.state.count]}</div>;

// Render your component
render(<App />, dom);
expect(dom.textContent).to.equal("count: 0");

// Trigger a state update

// Flush all state updates

expect(dom.textContent).to.equal("count: 1");

It's his first contribution to Preact and we couldn't be more ecstatic about his work. Give him a round of applause 👍 It's also good to know for us that our code remains very accessible for new contributors.


  • Fix devtools not loading (#1360, thanks @calebeby)
  • Inline vnode children (#1317, thanks @developit)
  • Remove double Promise on initialization (#1370, thanks @bmeurer and @marvinhagemeister)
  • Fix camelCase style properties not working (#1375, thanks @mochiya98)
  • Fix Portal rendering undefined tags into the DOM (#1367, thanks @marvinhagemeister)
  • Fix this.props not set for PureComponent components (#1384, thanks @marvinhagemeister)
  • Use console.error for caught errors in the devtools adapter (#1385, thanks @yuqianma)
  • Fix state not updated when setState is called in componentWillReceiveProps (#1369, thanks @mochiya98)
  • Fix svg foreignObject not being treated as svg (#1391, thanks @marvinhagemeister)
  • Fix onBeforeInput not attaching event handler (#1366, thanks @marvinhagemeister)
  • Fix React.isValidElement check failing for vnodes created with Preact (#1380, thanks @marvinhagemeister)
  • Fix <select>.value not working (#1397, thanks @JoviDeCroock)
  • Fix multiple selections for <select> only selecting last item (#1405, thanks @JoviDeCroock)
  • Fix force flag blocking state update in second render() (#1377, thanks @developit)
  • Fix IE11 crashing because of missing Element.prototype.remove (#1395, thanks @marvinhagemeister)
  • Fix forwardRef and memo ignoring refs when composed together (#1358, thanks @marvinhagemeister)


  • Move _prevState for devtools out of core (#1379, thanks @developit)
  • Reuse oldChildrenLength to save bytes (#1386, thanks @yuqianma)
  • Don't call setProperty for style strings (#1394, thanks @choumx)


  • Fix unable to satisfy type parameters for static methods (#1365, thanks @marvinhagemeister)
  • Add back missing camelCase attributes (#1390, thanks @marvinhagemeister)
  • Add missing autocorrect attribute types (#1393, thanks @garybernhardt)
  • Ignore final new line for all mangle.json files (#1378, thanks @developit)
  • Add more tests for componentWillReceiveProps (#1387, thanks @developit)

Phew what an exciting few days it has been! We've been blown away by the wonderful responses to our 10.0.0-alpha.0 release and are excited to ship the next one with a good dozen of bug fixes.

In just three days we received a lot of awesome PRs pushing Preact X forward 👍 This wouldn't be possible with the outstanding issues we've received. They all had a link to a codesandbox where the issue could be easily reproduced. This is every maintainer's dream and we can't stress enough how much time it saves us to fix bugs 🚀

  • Switch away from MessageChannel to setTimeout + requestAnimationFrame 🎉 (#1346, thanks @cristianbote)
  • Fix lifecycle order of componentWillReceiveProps in relation to shouldComponentUpdate 💯(#1348, thanks @mochiya98)
  • Fix incorrect operator precedence in memo() 🎉 (#1347, thanks again @mochiya98)
  • Preserve devtools global hook 🔧 (#1333, thanks @developit)
  • Fix missing server.js in npm package (#1332, thanks @developit)
  • Fix incorrect this binding in setState callback (#1344, thanks @marvinhagemeister)
  • Fix stale props and state reference when shouldComponentUpdate returns false (#1351, thanks @marvinhagemeister)
  • Don't leak Text nodes out of toChildArray (#1357, thanks @developit)
  • Add missing consistent mangle name for _parentDOM (#1320, thanks @andrewiggins)
  • Add hgroup to TypeScript typings (#1340, thanks @ForsakenHarmony)
  • Add missing isReactComponent-property in preact/compat (#1337, thanks @marvinhagemeister)
  • Downgrade IE11 to run on Windows 7 in Saucelabs (#1319, thanks @marvinhagemeister)

Preact X Alpha 0 is here!

tl;dr: Preact X is the next major release which comes with a plethora of highly requested features like Fragments, componentDidCatch, createContext, hooks and many compatibility improvements with third-party libraries.

Fragments ✅

Fragments has been the most requested feature for Preact for a long time and we were very keen on bringing them into Preact! With Preact X they are now finally here 🎉 Use the new Fragment export in your components to render children elements inline with their parent, without an extra wrapping DOM element. For example:

import { render, Fragment } from "preact";

function Table() {
  return (
        <Columns />

function Columns() {
  return (

render(<Table />, document.body);

// Resulting DOM:

You can also return arrays from your components:

function Columns() {
  return [

Don't forget to add keys to Fragments if you create them in a loop:

function Glossary(props) {
  return (
      { => (
        // Without the `key`, Preact can't efficiently add,
        // remove, remove new elements as the list changes
        <Fragment key={}>

Fragments are a major new feature of Preact X that largely motivated the rewrite
from Preact 8. We could really use a lot of help testing and validating our
Fragment implementation. If you find something that doesn't seem right, we would
really appreciate reporting a minimally reproducible example to help us improve
our implementation and messaging around Fragments.

componentDidCatch ✅

We all wish errors wouldn't exists in Web-Apps but sometimes they do happen. With componentDidCatch all errors that happen inside render or another lifecycle method can be caught. This can be used to display user-friendly error messages, or write a log entry to an external service in case something goes wrong.

class Foo extends Component {
  state = { error: false };

  componentDidCatch(err) {
    this.setState({ error: true });

  render() {
    // If an error happens somewhere down the tree
    // we display a nice error message.
    if (this.state.error) {
      return <div class="error">Something went wrong...</div>;

    return <Bar />;

Because setting a fallback content in an error case is so common we made this even nicer by adding for getDerivedStateFromError which calls setState automatically under the hood.

class Foo extends Component {
  state = { error: false };

  static getDerivedStateFromError(err) {
    // return argument for setState
    return { error: true } 

  render() {
    // If an error happens somewhere down the tree
    // we display a nice error message.
    if (this.state.error) {
      return <div class="error">Whoops! This should not have happened</div>;

    return <Bar />;


Preact now supports Hooks. React has some phenomenal documentation on hooks that's worth a read, particularly if you're getting started with them for the first time. In Preact, you import hooks from preact/hooks. If you're using a recent version of most bundlers, any hook functions you don't use won't be included in your application.

Here's what hooks look like in Preact:

import { h, render } from 'preact';
import { useState } from 'preact/hooks';

function Counter() {
  const [count, setCount] = useState(0);
                            // ^ default state value
  return (
    <div class="counter">
      Current count: {count}
      <button onClick={() => setCount(count + 1}}> +1 </button>
      <button onClick={() => setCount(count - 1}}> -1 </button>

render(<Counter />, document.body);

createContext ✅

The createContext-API is a true successor for getChildContext(). Whereas getChildContext is fine when you're absolutely sure to never change a value, it falls apart as soon as a component in-between the provider and consumer blocks an update via shouldComponentUpdate when it returns false. With the new context API this problem is now a thing of the past. It is a true pub/sub solution to deliver updates deep down the tree.

import { createContext } from "preact";

const Theme = createContext("red");

function Button() {
  return <Theme.Consumer>
    {value => <button style={{ color: value }}>click</button>}

function App() {
  return <Theme.Provider value="blue">
    <Button />

CSS Custom Properties ✅

Sometimes it's the little things that make a huge difference. With the recent advancements in CSS you can leverage variables for styling:

function Foo(props) {
  return <div style={{ "--theme-color": "blue" }}>{props.children}</div>;

Devtools Adapter

To be able to support all the recent advancements in the excellent react-devtools extension we knew we had to redo our devtools adapter. In previous version we disguised Preact as React v15 to connect to them but this was getting more and more difficult with features added in later releases like the Profiler tab.

For Preact X we rewrote our adapter from scratch and can directly hook into our own renderer. This is a lot more straightforward for us and eases feature development greatly. It didn't take long for us to bring the Profiler into Preact 👍

Compat lives now in core

Although we were always keen on adding new features and pushing Preact
forward, the preact-compat package didn't receive as much love. Up until now
it has lived in a separate repository making it harder to introduce breaking

// Preact 8.x
import React from "preact-compat";

// Preact X
import React from "preact/compat";

New compat features

  • forwardRef
  • UNSTABLE_*-Lifecycle hooks
  • memo

Removing old React APIs

To make maintenance easier we dropped all legacy APIs that were available
in React versions prior to v16. This includes the DOM-Factories API,
createClass, string refs and a few more.

Breaking Changes

We were very careful to introduce as few breaking changes as possible. As a user
the most noticable change will be that props.children is not guaranteed to be
an array anymore. This change was necessary to be able to support rendering
components that return an array of children without wrapping them in a
root node. On top of that this fixes quite a few issues with third-party
components that expect props.children to be undefined when no children are
passed around.

The VNode shape has changed

We renamed/moved the following properties:

  • attributes -> props
  • nodeName -> type
  • children -> props.children

The children of a VNode are no longer guaranteed to be a flat array.
props.children could be undefined or it could be a nested array of children.
Pass props.children to the newly exported helper toChildArray to always get
an array back.

import { h, toChildArray } from "preact";

function MyComponent(props) {
	// Always convert props.children to an array
	const children = toChildArray(props.children);
	return <div>I have {children.length} child nodes</div>;

Note: toChildArray will flatten and remove non-renderables like null, undefined, true, and false from the children array.

setState no longer modifies state synchronously

In Preact X the state of a component will no longer be mutated synchronously.
This means that reading from this.state right after a setState call will
return the previous values. Instead you should use a callback function to
modify state that depends on the previous values.

this.state = { counter: 0 };

// Preact 8.x
this.setState({ counter: this.state.counter++ });

// Preact X
this.setState(prevState => {
	// Alternatively return `null` here to abort the state update
	return { counter: prevState.counter++ };

render() has changed

The root render function (the one you import from preact) has changed. It no
longer returns the newly created DOM element. Its return type is now void. We
made it simpler, by removing the third argument that was used to hydrate the
DOM. Use the hydrate function instead to hydrate a server rendered DOM tree.

When render is called multiple times on the same elements, the trees will be
merged together. It no longer just appends to the DOM. This change will be
very welcomed by new users as it was a frequeuent source confusion 🎉

import { render, hydrate } from "preact";

const root = document.getElementById("root");

// Render into the DOM
render(<div>foo</div>, root);
// calling `render` a second time will merge the trees like you would expect it to
render(<div>foo<span>bar</span></div>, root);

// Or use `hydrate` if you're making use of server side rendering
hydrate(<div>foo</div>, root);

preact/devtools is part of preact/debug

Our devtools adapter has been moved into our debug package and doesn't require a dedicated import statement anymore. The preact/debug package must be imported before preact to prevent our devtools integration to be overwritten by the default one supplied by the react-devtools extension.

Better support for Tree-Shaking

A lot has changed in the past years in the bundling space. Most bundlers now
offer excellent support for tree-shaking, where unused exports can be dropped
if they are not used. In the past we always exported an additional object as
the default export. But because of the nature of JavaScript it is very hard
to prove that an object property will never be used, they were never removed.

By removing the default export completely, only the code you need will
be included in the bunlde. The rest will be tree-shaken away.

// Preact 8.x
import preact from "preact";

// Preact X
import * as preact from "preact";

// Preferred: Named exports (works in 8.x and Preact X)
import { h, Component } from "preact";

Note: This change doesn't affect preact/compat. It still has both named and a default export to remain compatible with react.

Other breaking changes

  1. Falsy attributes values are no longer removed from the DOM. For some
    attributes (e.g. spellcheck) the values false and '' have different
    meaning so being able to render false is important
  2. The Component constructor no longer initializes state to an empty object. If
    state has not been previously set, it will be set to an empty object the first
    time the component is rendered, after the constructor has been called
  3. A falsy argument passed to setState will not enqueue an update. This change was made to support returning null from the updater function, which allows users to abort an update. If you just want to trigger an update and don't care about the state itself you can call it with an empty object setState({}).
  4. The toplevel rerender export has been removed.

Minor Changes

  1. render(null, container) no longer renders an empty text node but instead renders nothing
  2. We longer support a synchronous options.debounceRendering. The value of options.debounceRendering must asynchronously invoke the passed in callback. It is important that contributors to Preact can consistently reason about what calls to setState, etc. do, and when their effects will be applied. See the links below for some
    further reading on designing asynchronous APIs.

Known Issues

  • Nested Fragments lead to more DOM operations than necessary
  • Hooks state not visible inside the devtools panel