fix(frames): hand slot claims over from the enclosing hydration registry - #2961
Merged
ryansolid merged 2 commits intoJul 31, 2026
Merged
Conversation
`gatherHydratable` sweeps the whole document for `_hk` and does not skip frame regions, so every client slot root inside a server component lands in the page-wide registry too. Adoption claims those nodes through the scoped registry `claimRender` gathers from the slot range, which leaves the root registry's copies untouched — and the end-of-hydration check then reports each correctly-claimed slot node as unclaimed server markup. Drop the scoped registry's keys from the enclosing one when the claim scope takes ownership. Only the scoped pass ever claims them, so nothing else loses a claim, and the dev warning stops firing on every load of an app with client components inside server components (the notes example warned about all nine). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
The sidebar filter IS the `?searchText` query param — the root preload reads it, so the notes-list server component refetches whenever it changes. The note links and the New/Edit buttons dropped it, so clicking a note mid-search cleared the box and morphed the list back to the unfiltered set. Route navigation state through a small useSearchLink() helper that appends the current filter. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
brenelz
commented
Jul 31, 2026
| // up in the root registry too. Only this scoped registry ever claims them, | ||
| // so hand ownership over — otherwise the root's completion check reports | ||
| // each claimed slot node as unclaimed server markup. | ||
| if (prevRegistry) for (const key of registry.keys()) prevRegistry.delete(key); |
Contributor
Author
There was a problem hiding this comment.
maybe this is a bad fix
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.
Problem
Every page load of an app with client components inside server components warns, even though hydration is correct. The
examples/notesdev server reports all nine of its client slot roots:The listed nodes are exactly the client slot roots —
SearchField,EditButton, eachSidebarNoteContentand itsShowfallback, and the route outlet. They are claimed in place and fully live (the expand toggle, the search field and the route outlet all work); only the accounting is wrong.Cause
hydrate()builds the page-wide registry withgatherHydratable, which is a blanketquerySelectorAll("*[_hk]")over the container — it does not skip frame regions, so every server component's client slot roots land in the root registry.Adoption never claims from that registry.
claimRendergathers the slot range's own_hknodes into a scoped registry, swaps it in for the synchronous render, and restores the previous one after.getNextElementdeletes the key it claims from whichever registry is current, so the claim consumes the scoped copy and the root registry's copy survives to the end of hydration — whereverifyHydrationreports it as unclaimed server markup.Fix
When the claim scope takes ownership of a range, drop its keys from the enclosing registry. Only the scoped pass ever claims those nodes, so nothing else loses a claim.
Test plan
examples/notesunderpnpm dev: warning gone, sidebar expand/collapse, search and navigation still work.🤖 Generated with Claude Code