You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
We need tree-shaking in preact/compat. A lot of the options hooks are exclusive to a given feature. In this PR we target use/uSES/Suspense.
Measured from the production compat/dist/compat.mjs generated by npm run build, then tree-shaken with Rollup's defaults, minified with Terser, and compressed with gzip:
useState: 2146 B → 1551 B (-595 B / 27.7%)
forwardRef: 2248 B → 1650 B (-598 B / 26.6%)
memo: 2262 B → 1674 B (-588 B / 26.0%)
useSyncExternalStore: 2341 B → 1785 B (-556 B / 23.8%)
use: 2280 B → 1734 B (-546 B / 23.9%)
Suspense: 2154 B → 2159 B (+5 B / 0.2%)
lazy: 2241 B → 1649 B (-592 B / 26.4%)
The production build preserves the /* @__PURE__ */ initializers. These results use Rollup's default propertyReadSideEffects: true; setting it to false produced identical output.
The compressed-size check measures the complete compat artifact, where every export is present, so it instead reports +62 B gzip. The build reports +67 B Brotli, and a downstream consumer retaining the default export grows by 38 B.
Notes
In an ideal world we could also remove the options.event, options.vnode, and textarea options.diffed hooks from the base cost of compat. That would let people select individual React compatibility features without incurring the cost of those hooks.
A production-build experiment gating all three behind pure render/hydrate initializers produced these representative gzip results compared with this PR:
useState: 1551 B → 369 B (-1182 B)
forwardRef: 1650 B → 496 B (-1154 B)
Suspense: 2159 B → 1026 B (-1133 B)
render: 1609 B → 1628 B (+19 B)
default import: unchanged
complete compat artifact: 3916 B → 3930 B (+14 B)
This would be a breaking change. If an application does not retain render or hydrate from compat, isValidElement(createElement('div')) can return false. It can also skip $$typeof, defaultProps, class-component ref handling, DOM prop normalization, React event normalization, and the textarea fix when compat VNodes are consumed by another or separately bundled renderer.
Installing the hooks inside the runtime render() call is too late because the root VNode, and often its children, have already been created. If we want this, we should chase it separately.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Note
The idea was created by Jovi and this PR was authored by GPT5.6-sol
Resolves #1529
Description
We need tree-shaking in preact/compat. A lot of the options hooks are exclusive to a given feature. In this PR we target use/uSES/Suspense.
Measured from the production
compat/dist/compat.mjsgenerated bynpm run build, then tree-shaken with Rollup's defaults, minified with Terser, and compressed with gzip:useState: 2146 B → 1551 B (-595 B / 27.7%)forwardRef: 2248 B → 1650 B (-598 B / 26.6%)memo: 2262 B → 1674 B (-588 B / 26.0%)useSyncExternalStore: 2341 B → 1785 B (-556 B / 23.8%)use: 2280 B → 1734 B (-546 B / 23.9%)Suspense: 2154 B → 2159 B (+5 B / 0.2%)lazy: 2241 B → 1649 B (-592 B / 26.4%)The production build preserves the
/* @__PURE__ */initializers. These results use Rollup's defaultpropertyReadSideEffects: true; setting it tofalseproduced identical output.The compressed-size check measures the complete compat artifact, where every export is present, so it instead reports +62 B gzip. The build reports +67 B Brotli, and a downstream consumer retaining the default export grows by 38 B.
Notes
In an ideal world we could also remove the
options.event,options.vnode, and textareaoptions.diffedhooks from the base cost of compat. That would let people select individual React compatibility features without incurring the cost of those hooks.A production-build experiment gating all three behind pure
render/hydrateinitializers produced these representative gzip results compared with this PR:useState: 1551 B → 369 B (-1182 B)forwardRef: 1650 B → 496 B (-1154 B)Suspense: 2159 B → 1026 B (-1133 B)render: 1609 B → 1628 B (+19 B)This would be a breaking change. If an application does not retain
renderorhydratefrom compat,isValidElement(createElement('div'))can returnfalse. It can also skip$$typeof,defaultProps, class-component ref handling, DOM prop normalization, React event normalization, and the textarea fix when compat VNodes are consumed by another or separately bundled renderer.Installing the hooks inside the runtime
render()call is too late because the root VNode, and often its children, have already been created. If we want this, we should chase it separately.