fix(compiler): align ref spread hydration ids - #3228
Open
kavhnr wants to merge 1 commit into
Open
Conversation
🦋 Changeset detectedLatest commit: 0824eec The changes in this PR will be included in the next version bump. This PR includes changesets to release 11 packages
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
kavhnr
force-pushed
the
fix/ref-spread-hydration-parity
branch
from
September 2, 2026 19:43
d864f5d to
0824eec
Compare
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
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
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.
Summary
An explicit intrinsic
refmakes SSR miss the lone-spread fast path. The DOM transform handlesrefseparately and still passes one source directly tospread(). SSR counts the raw JSX attributes and sends the same source throughmergeProps.This change makes the Babel and native SSR transforms ignore explicit
refattributes only when they decide if one spread source can pass through. Other attributes still keep the merge path.Reproduction
Before this change, the relevant output is:
mergePropscreates a memo for the function source. On the server, thedivgets key0, that memo consumes ID1, and the button gets key2. The client does not create the memo and expects the button at key1. In the hydration harness, the server button stayed atbeforeafter its click because the client did not claim it with the expected key.After this change, SSR emits:
The server output now has consecutive keys
0and1. The same nodes hydrate without warnings, the following click handler runs, and the signal update changes the text toafter.Invariant
A single effective DOM spread source passes directly to
spread()andssrElement(). An explicit intrinsicref, including a ref directive factory, is not a spread source because the DOM transform applies it through the ref path. Ordinary attributes, events,prop:*, explicit children, and additional spreads still add DOM sources. They still usemergePropson both sides.The compiler is the narrow layer that knows this source count. A runtime change to
mergePropswould also change valid multi-source and component behavior. Restoring a client merge for this case would undo the single-source direction established by 5230666.PR #3105 found the memo and hydration-ID mismatch, but it proposed adding the missing merge to SSR. Commit 5230666 fixed that case in the other direction: it removed the unnecessary client merge. This change keeps that direction and covers the explicit-
refedge that its regression case did not include.Coverage
prop:*, explicit children, multiple attributes and spreads, and components.How did you test this change?
These commands passed:
pnpm install --frozen-lockfilepnpm --filter @solidjs/babel-plugin testpnpm --filter @solidjs/compiler testpnpm --filter @solidjs/compiler lintpnpm --filter @solidjs/web testJSX_COMPILER=babel pnpm --filter @solidjs/web testpnpm typecheckpnpm buildpnpm testpnpm test:integrationpnpm exec prettier --check packages/babel-plugin/src/ssr/element.ts packages/babel-plugin/test/ref-spread.spec.js packages/compiler/__tests__/parity-probes.test.js packages/web/test/harness/scenarios.tsx .changeset/align-ref-spread-sources.md packages/web/test/harness/__artifacts__/reactive-ref-lone-spread-id-parity.jsonrustfmt --edition 2024 --check packages/compiler/src/ssr/transform.rsgit diff --checkI also ran
npm run sizeinscripts/size. It reports the same ten limit overruns on this commit and on the exact base commit, 981718b. The values are identical: 14–215 bytes over the current limits. This compiler-only change does not change those runtime bundles.