Skip to content

[Fizz] Support nested enter/exit ViewTransition animations - #36917

Merged
jackpope merged 4 commits into
react:mainfrom
jackpope:parent-enter-exit-vt-ssr
Jul 19, 2026
Merged

[Fizz] Support nested enter/exit ViewTransition animations#36917
jackpope merged 4 commits into
react:mainfrom
jackpope:parent-enter-exit-vt-ssr

Conversation

@jackpope

@jackpope jackpope commented Jul 1, 2026

Copy link
Copy Markdown
Collaborator

Adds SSR support for nested parentEnter/parentExit View Transitions. Fizz now emits vt-parent-enter/vt-parent-exit annotations during streaming, and the client picks them up on hydration, so nested enter/exit animations work for Suspense reveals.

@meta-cla meta-cla Bot added the CLA Signed label Jul 1, 2026
@github-actions github-actions Bot added the React Core Team Opened by a member of the React Core Team label Jul 1, 2026
@react-sizebot

react-sizebot commented Jul 1, 2026

Copy link
Copy Markdown

Comparing: 689a4fa...025afa2

Critical size changes

Includes critical production bundles, as well as any change greater than 2%:

Name +/- Base Current +/- gzip Base gzip Current gzip
oss-stable/react-dom/cjs/react-dom.production.js = 7.19 kB 7.19 kB = 1.91 kB 1.91 kB
oss-stable/react-dom/cjs/react-dom-client.production.js = 614.21 kB 614.21 kB = 108.58 kB 108.58 kB
oss-experimental/react-dom/cjs/react-dom.production.js = 7.19 kB 7.19 kB = 1.91 kB 1.91 kB
oss-experimental/react-dom/cjs/react-dom-client.production.js = 685.39 kB 685.39 kB = 120.07 kB 120.07 kB
facebook-www/ReactDOM-prod.classic.js = 705.24 kB 705.24 kB = 123.51 kB 123.51 kB
facebook-www/ReactDOM-prod.modern.js = 695.56 kB 695.56 kB = 121.90 kB 121.90 kB
oss-experimental/react-dom/unstable_server-external-runtime.js +4.51% 18.15 kB 18.97 kB +2.52% 4.13 kB 4.23 kB

Significant size changes

Includes any change greater than 0.2%:

Expand to show
Name +/- Base Current +/- gzip Base gzip Current gzip
oss-experimental/react-dom/unstable_server-external-runtime.js +4.51% 18.15 kB 18.97 kB +2.52% 4.13 kB 4.23 kB
facebook-www/ReactDOMServerStreaming-prod.modern.js +1.03% 271.52 kB 274.31 kB +0.93% 49.44 kB 49.90 kB
oss-experimental/react-dom/cjs/react-dom-server.bun.production.js +0.71% 294.92 kB 297.01 kB +0.72% 51.66 kB 52.03 kB
facebook-www/ReactDOMServerStreaming-dev.modern.js +0.70% 430.92 kB 433.92 kB +0.63% 76.99 kB 77.48 kB
oss-experimental/react-dom/cjs/react-dom-server.node.production.js +0.68% 308.33 kB 310.42 kB +0.71% 54.05 kB 54.44 kB
oss-experimental/react-dom/cjs/react-dom-server.browser.production.js +0.65% 294.04 kB 295.94 kB +0.74% 51.75 kB 52.13 kB
oss-experimental/react-dom/cjs/react-dom-server.edge.production.js +0.63% 300.61 kB 302.51 kB +0.70% 54.24 kB 54.62 kB
oss-experimental/react-dom/cjs/react-dom-server.browser.development.js +0.46% 462.24 kB 464.39 kB +0.47% 81.49 kB 81.88 kB
oss-experimental/react-dom/cjs/react-dom-server.bun.development.js +0.46% 409.11 kB 411.01 kB +0.52% 76.83 kB 77.23 kB
oss-experimental/react-dom/cjs/react-dom-server.edge.development.js +0.46% 463.25 kB 465.40 kB +0.46% 81.72 kB 82.10 kB
oss-experimental/react-dom/cjs/react-dom-server.node.development.js +0.46% 469.00 kB 471.15 kB +0.50% 81.50 kB 81.90 kB
facebook-www/ReactDOMServer-prod.modern.js +0.37% 261.23 kB 262.19 kB +0.34% 46.54 kB 46.69 kB
facebook-www/ReactDOMServer-prod.classic.js +0.37% 263.56 kB 264.52 kB +0.34% 46.88 kB 47.04 kB
oss-experimental/react-markup/cjs/react-markup.production.js +0.27% 251.76 kB 252.44 kB +0.29% 46.50 kB 46.64 kB
oss-experimental/react-dom/cjs/react-dom-server-legacy.browser.production.js +0.26% 264.49 kB 265.17 kB +0.25% 47.19 kB 47.31 kB
oss-experimental/react-dom/cjs/react-dom-server-legacy.node.production.js +0.25% 270.26 kB 270.94 kB +0.25% 49.40 kB 49.52 kB
oss-experimental/react-server/cjs/react-server.production.js +0.23% 154.74 kB 155.10 kB +0.17% 27.20 kB 27.25 kB

Generated by 🚫 dangerJS against 025afa2

@jackpope
jackpope requested review from gnoff and zeyap July 1, 2026 16:03
@jackpope
jackpope force-pushed the parent-enter-exit-vt-ssr branch from 5604455 to d678a75 Compare July 1, 2026 16:18
@jackpope
jackpope requested a review from eps1lon July 1, 2026 16:19
Comment on lines +2955 to +2965
props.parentEnter !== undefined
? getViewTransitionClassName(props.default, props.parentEnter)
: undefined,
props.parentExit !== undefined
? getViewTransitionClassName(props.default, props.parentExit)
: undefined,
// A ViewTransition with an onParentEnter/onParentExit handler but no class
// still relays the activation to its descendants, so the relay must continue
// through it even though the handler itself emits no annotation.
props.onParentEnter != null,
props.onParentExit != null,

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should these be gated on the feature flag?

Comment on lines +244 to +245
htmlElement.getAttribute('vt-parent-enter') ||
htmlElement.getAttribute('vt-parent-exit')

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Prob missing the feature flag?

Comment on lines +235 to +240
// Relay the exit to nested ViewTransitions that opted in
const relayExitElements =
exitElement.querySelectorAll('[vt-parent-exit]');
for (let j = 0; j < relayExitElements.length; j++) {
applyViewTransitionName(relayExitElements[j], 'vt-parent-exit');
}

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we have the infra to have an experimental version?

@jackpope jackpope Jul 9, 2026

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It doesn't look like it. So the instruction set will always be able to handle the annotations, but there won't be any outside of experimental

daltino

This comment was marked as spam.

@jackpope
jackpope force-pushed the parent-enter-exit-vt-ssr branch from 4c2cfed to 2b9e4ef Compare July 6, 2026 20:09
@jackpope
jackpope force-pushed the parent-enter-exit-vt-ssr branch from 2b9e4ef to 6665441 Compare July 8, 2026 15:48
jackpope added 2 commits July 8, 2026 11:57
Adds vt-parent-enter/vt-parent-exit SSR scope propagation and client
hydration support for the parent enter/exit props.
@eps1lon

eps1lon commented Jul 8, 2026

Copy link
Copy Markdown
Collaborator

Can you double check why this hits the stacktrace limit earlier now? Iirc Meta hits those limits regularly. We want to pay extra attention if this somehow doubles the stackframes used per Component.

@jackpope

Copy link
Copy Markdown
Collaborator Author

@eps1lon Ran some more comparisons locally. This isn't increasing the number of frames, its adding a bit of overhead to the functions which bumps the amount of frames needed to overflow slightly in the extreme recursion case. In one local test it overflowed at 537 levels vs 545 before (so certainly not double), but updated handling in #36977 allows recovering, and that change should be a general improvement for the Meta case, especially based on the old test TODO.

cc @hoxyq that these two PRs will need monitoring when syncing in

@eps1lon eps1lon left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for checking! I couldn't spot if we added frames so this was confusing at first. But makes sense that there's also a constant factor wrt to frame size.

jackpope added a commit that referenced this pull request Jul 19, 2026
Ran into this test failure as part of
#36917 - it seems that the added code
was just enough to increase stack size and fail the deep tree recovery
test in CI. Looking into that, there appears to be a gap here with
retries, including a TODO test case for the scenario.

Fizz recovers from stack overflows in extremely deep trees by catching
the first overflow in the `renderNode` trampoline and spawning a
continuation task. That continuation is retried via `retryRenderTask β†’
retryNode`, which has no trampoline above it. So if the remaining tree
still doesn't fit in one fresh stack, the overflow was treated as a
fatal error instead of recovering again.

This fix re-schedules the task when a retried render overflows but
`task.node` advanced (proving forward progress was made). If this is a
real in-component overflow, `task.node` doesn't advance and we still
fail.

The existing test used `n={1000}`, which only required one recovery
round and didn't catch this gap in source mode. It's updated to
`n={1200}`, which reliably requires multiple recovery rounds.
github-actions Bot pushed a commit that referenced this pull request Jul 19, 2026
Ran into this test failure as part of
#36917 - it seems that the added code
was just enough to increase stack size and fail the deep tree recovery
test in CI. Looking into that, there appears to be a gap here with
retries, including a TODO test case for the scenario.

Fizz recovers from stack overflows in extremely deep trees by catching
the first overflow in the `renderNode` trampoline and spawning a
continuation task. That continuation is retried via `retryRenderTask β†’
retryNode`, which has no trampoline above it. So if the remaining tree
still doesn't fit in one fresh stack, the overflow was treated as a
fatal error instead of recovering again.

This fix re-schedules the task when a retried render overflows but
`task.node` advanced (proving forward progress was made). If this is a
real in-component overflow, `task.node` doesn't advance and we still
fail.

The existing test used `n={1000}`, which only required one recovery
round and didn't catch this gap in source mode. It's updated to
`n={1200}`, which reliably requires multiple recovery rounds.

DiffTrain build for [689a4fa](689a4fa)
@jackpope
jackpope requested a review from eps1lon July 19, 2026 17:46
@jackpope
jackpope merged commit 8384090 into react:main Jul 19, 2026
238 checks passed
@jackpope
jackpope deleted the parent-enter-exit-vt-ssr branch July 19, 2026 19:59
github-actions Bot pushed a commit that referenced this pull request Jul 19, 2026
Adds SSR support for nested parentEnter/parentExit View Transitions.
Fizz now emits vt-parent-enter/vt-parent-exit annotations during
streaming, and the client picks them up on hydration, so nested
enter/exit animations work for Suspense reveals.

DiffTrain build for [8384090](8384090)
github-actions Bot pushed a commit that referenced this pull request Jul 19, 2026
Adds SSR support for nested parentEnter/parentExit View Transitions.
Fizz now emits vt-parent-enter/vt-parent-exit annotations during
streaming, and the client picks them up on hydration, so nested
enter/exit animations work for Suspense reveals.

DiffTrain build for [8384090](8384090)
github-actions Bot pushed a commit to code/lib-react that referenced this pull request Jul 20, 2026
Adds SSR support for nested parentEnter/parentExit View Transitions.
Fizz now emits vt-parent-enter/vt-parent-exit annotations during
streaming, and the client picks them up on hydration, so nested
enter/exit animations work for Suspense reveals.

DiffTrain build for [8384090](react@8384090)
github-actions Bot pushed a commit to code/lib-react that referenced this pull request Jul 20, 2026
Adds SSR support for nested parentEnter/parentExit View Transitions.
Fizz now emits vt-parent-enter/vt-parent-exit annotations during
streaming, and the client picks them up on hydration, so nested
enter/exit animations work for Suspense reveals.

DiffTrain build for [8384090](react@8384090)
github-actions Bot pushed a commit to srpatcha/react that referenced this pull request Jul 20, 2026
Adds SSR support for nested parentEnter/parentExit View Transitions.
Fizz now emits vt-parent-enter/vt-parent-exit annotations during
streaming, and the client picks them up on hydration, so nested
enter/exit animations work for Suspense reveals.

DiffTrain build for [8384090](react@8384090)
github-actions Bot pushed a commit to srpatcha/react that referenced this pull request Jul 20, 2026
Adds SSR support for nested parentEnter/parentExit View Transitions.
Fizz now emits vt-parent-enter/vt-parent-exit annotations during
streaming, and the client picks them up on hydration, so nested
enter/exit animations work for Suspense reveals.

DiffTrain build for [8384090](react@8384090)
gaearon added a commit to gaearon/react that referenced this pull request Jul 20, 2026
Resolves the semantic conflict between the open-tag fusion commit and
react#36917: the new vt-parent-enter/vt-parent-exit attributes now emit from
viewTransitionAttributesToString (as part of the fused open tag) instead
of the pushViewTransitionAttributes wrapper, where the auto-merge had
stranded them without a viewTransition binding.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

CLA Signed React Core Team Opened by a member of the React Core Team

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants