Skip to content

Fix clientEntry collision for entries sharing an entry id#11524

Open
vismayIO wants to merge 2 commits into
remix-run:mainfrom
vismayIO:fix/cliententry-same-file-collision
Open

Fix clientEntry collision for entries sharing an entry id#11524
vismayIO wants to merge 2 commits into
remix-run:mainfrom
vismayIO:fix/cliententry-same-file-collision

Conversation

@vismayIO

@vismayIO vismayIO commented Jun 5, 2026

Copy link
Copy Markdown

Problem

Fixes #11503

Two clientEntry(import.meta.url, namedFn) exports declared in the same file and rendered in the same SSR pass both hydrate as the first one. The server renders each correctly, but on hydration the second's slot adopts the first component, causing a hydration mismatch and the wrong interactive component on the client.

resolveClientEntries memoized resolution in a Map keyed by entryId only (packages/ui/src/server/stream.ts). A bare import.meta.url makes entryId the file, so two clientEntries in one file share it: the hook runs once (with the first component) and the second instance reuses that result; the second's component.name is never looked at. Both entries stream exportName: "Alpha".

Fix

Key the resolution cache by the component reference (the unique identity) instead of the entryId string:

  • Same component rendered N times → same reference → still one resolution (dedup preserved).
  • Two distinct components sharing one entryId → different references → resolved independently with their own exportName.

Client-side hydration was already correct (keyed per unique instance id); the corruption was purely in SSR resolution.

Tests

  • Added: resolves two clientEntries sharing one entry id to distinct exports during SSR — fails before the fix (both resolve to Alpha), passes after.
  • Existing pinning test calls resolveClientEntry only once per unique entry id during SSR still passes (same component rendered 3× → one resolve call).
  • Full @remix-run/ui suite: 792 pass, 0 fail.

Two clientEntry() calls in the same file share an entry id (e.g. a bare
import.meta.url), but are distinct components that must resolve to distinct
exports. resolveClientEntries memoized resolution keyed by entry id, so the
second entry adopted the first's resolution, producing the wrong client
component and a hydration mismatch.

Key the resolution cache by the component reference (the unique identity)
instead of the entry id. Rendering the same component N times still hits the
cache (one resolution per distinct component), while two components sharing
an entry id now resolve independently.

Fixes remix-run#11503

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@brookslybrand brookslybrand review requested due to automatic review settings June 5, 2026 14:18
@vismayIO

vismayIO commented Jun 9, 2026

Copy link
Copy Markdown
Author

@markdalgleish and @ryanflorence, Could you please share any updates on this PR? Also, let me know if there’s anything else I can do to help resolve the issue.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Two clientEntries in the same file collide when resolved by import.meta.url

1 participant