Skip to content

Fix false hydration mismatch when client renders portal that returned null on server#36321

Open
starboyvarun wants to merge 4 commits intofacebook:mainfrom
starboyvarun:12615_portal_hydration_warning
Open

Fix false hydration mismatch when client renders portal that returned null on server#36321
starboyvarun wants to merge 4 commits intofacebook:mainfrom
starboyvarun:12615_portal_hydration_warning

Conversation

@starboyvarun
Copy link
Copy Markdown

@starboyvarun starboyvarun commented Apr 21, 2026

Summary

Fixes #12615

When a component returns null on the server but renders a ReactDOM.createPortal() on the client, React was incorrectly emitting a hydration mismatch error:

Hydration failed because the server rendered HTML didn't match the client.
  <span>
    <HoverMenu>
+     <div>   ← false positive: this is portal content, not a span child

This is a false positive — the portal's <div> renders into a separate container, not into the parent <span>. It should not be compared against the parent's server HTML.

Root Cause

When updatePortalComponent calls pushHostContainer to switch to the portal's container, the hydration state variables (isHydrating, nextHydratableInstance, hydrationParentFiber) were not reset. The portal's children then tried to claim hydration instances from the parent container's server nodes, which failed and triggered a mismatch error.

Fix

Added two functions to ReactFiberHydrationContext:

  • prepareToHydrateHostPortal — called in updatePortalComponent (begin work). Saves the current hydration state onto a stack and resets it, so portal children are inserted fresh rather than matched against server HTML.
  • popHydrationStateAfterPortal — called in the HostPortal case of complete work. Restores the saved hydration state so the parent resumes hydrating correctly after the portal.

This correctly handles nested portals (via the stack) and cleans up on resetHydrationState (work loop interruption).

Test Plan

  • Added two new tests in ReactDOMHydrationDiff-test.js:
    • Portal renders to a separate container → no mismatch warning
    • Portal renders to the hydration root container → no mismatch warning
  • All 39 hydration diff tests pass
  • All 183 tests across ReactServerRenderingHydration, ReactDOMServerPartialHydration, and ReactDOMFiber suites pass

…was null on server

Portals render into a separate container that is never server-rendered.
Previously, when a component returned null on the server but a portal on
the client, React would incorrectly try to hydrate the portal's children
against the parent container's server nodes, producing a false
'Hydration failed' error.

Fix: save and reset hydration state when entering a portal
(prepareToHydrateHostPortal), then restore it when the portal completes
(popHydrationStateAfterPortal). This ensures portal children are inserted
fresh rather than incorrectly matched against server HTML.

Fixes facebook#12615
@meta-cla
Copy link
Copy Markdown

meta-cla Bot commented Apr 21, 2026

Hi @starboyvarun!

Thank you for your pull request and welcome to our community.

Action Required

In order to merge any pull request (code, docs, etc.), we require contributors to sign our Contributor License Agreement, and we don't seem to have one on file for you.

Process

In order for us to review and merge your suggested changes, please sign at https://code.facebook.com/cla. If you are contributing on behalf of someone else (eg your employer), the individual CLA may not be sufficient and your employer may need to sign the corporate CLA.

Once the CLA is signed, our tooling will perform checks and validations. Afterwards, the pull request will be tagged with CLA signed. The tagging process may take up to 1 hour after signing. Please give it that time before contacting us about it.

If you have received this in error or have any questions, please contact us at cla@meta.com. Thanks!

prepareToHydrateHostPortal and popHydrationStateAfterPortal were already
exported via `export function`, so they must not also appear in the
trailing `export {}` block.
@meta-cla meta-cla Bot added the CLA Signed label Apr 22, 2026
@meta-cla
Copy link
Copy Markdown

meta-cla Bot commented Apr 22, 2026

Thank you for signing our Contributor License Agreement. We can now accept your code for this (and any) Meta Open Source project. Thanks!

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Unexpected warning when hydrating with portal and SSR

1 participant