fix: Render the Portal probe span during SSR - #249
Merged
NathanZlion merged 1 commit intoAug 18, 2026
Conversation
The probe span added in cloudscape-design#214 was gated on `typeof document !== 'undefined'`, so the server rendered nothing while the client's first render rendered the span. React reports that as a hydration mismatch and discards the subtree. Rendering the span on both passes makes the markup agree. It is display:none and is replaced by the portal on the first commit. Fixes AWSUI-62219
TrevorBurnham
marked this pull request as ready for review
August 15, 2026 22:13
TrevorBurnham
requested review from
NathanZlion
and removed request for
a team
August 15, 2026 22:13
Contributor
Author
|
cloudscape-design/components#4908 should be merged first to prevent tests from breaking downstream. |
NathanZlion
approved these changes
Aug 17, 2026
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #249 +/- ##
=======================================
Coverage 98.09% 98.09%
=======================================
Files 56 56
Lines 1787 1787
Branches 517 495 -22
=======================================
Hits 1753 1753
Misses 34 34 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
Contributor
Author
|
I believe the dry run failure ("Components unit tests") is because cloudscape-design/components#4908 hasn't been merged yet. |
NathanZlion
enabled auto-merge
August 18, 2026 15:55
Merged
via the queue into
cloudscape-design:main
with commit Aug 18, 2026
744ad19
115 of 118 checks passed
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.
Description
Fixes AWSUI-62219.
Portalrenders a hidden probe span on its first render, so the layout effect can readref.current.ownerDocumentand find the right document inside an iframe. That span was added in #214 and gated ontypeof document !== 'undefined':activeContaineris populated in a layout effect, so it's null on the first render. On the client that returns the span, but during SSR the guard fails and the component returns null. That causes React to emit th eerrorand discard and re-renders the subtree.
This affects every SSR consumer that renders a portal, including ones with no application-level workaround. Two paths in
@cloudscape-design/components:Modalwithvisible={false}, which many apps mount unconditionally.TablewithresizableColumns, viaStickyHeader→Resizer→DragHandleWrapper→PortalOverlay→Portal.Before #214, the only return was
activeContainer && createPortal(children, activeContainer), which yielded null on both passes.The fix here drops the
typeof documentcondition so the span renders during SSR too, and both passes agree. The cost is onedisplay: nonespan in the server HTML, replaced by the portal on the first commit. The iframe behavior #214 added is unchanged: the effect still reads the ref.How has this been reproduced and tested?
With any React 18+ SSR setup:
Before, React logs the mismatch and shows an added hidden
spanunderPortalin the diff. After, there's no mismatch.This PR adds unit test coverage to prevent regressions:
src/internal/portal/__tests__/portal.ssr.test.tsx.