Skip to content

[flags] Add enableParallelTransitions - #35392

Merged
rickhanlonii merged 4 commits into
mainfrom
ff-parallel-transitions
Feb 4, 2026
Merged

[flags] Add enableParallelTransitions#35392
rickhanlonii merged 4 commits into
mainfrom
ff-parallel-transitions

Conversation

@rickhanlonii

@rickhanloniirickhanlonii commented Dec 19, 2025

Copy link
Copy Markdown
Contributor

Overview

Adds a feature flag enableParallelTransitions to experiment with engantling transitions less often.

Motivation

Currently we over-entangle transition lanes.

It's a common misunderstanding that React entangles all transitions, always. We actually will complete transitions independently in many cases. For example, this codepen from @gabbev shows transitions completing independently.

However, in many cases we entangle when we don't need to, instead of letting the independent transitons complete independently. We still want to entangle for updates that happen on the same queue.

Example

As an example of what this flag would change, consider two independent counter components:

functionCounter({ label }){const[count,setCount]=useState(0);return(<div><span>{use(readCache(`${label}${count}`))}</span><Buttonaction={()=>{setCount((c)=>c+1);}}>
Next {label}</Button></div>);}
exportdefaultfunctionApp(){return(<><Counterlabel="A"/><Counterlabel="B"/></>);}

Before

The behavior today is to entange them, meaning they always commit together:

Screen.Recording.2026-01-22.at.11.12.34.AM.mov

After

In this experiment, they will complete independently (if they don't depend on each other):

Screen.Recording.2026-01-22.at.11.15.01.AM.mov

Early Research

This change is in early research, and is not in the experimental channel. We're going to experiment with this at Meta to understand how much of a breaking change, and how beneficial it is before commiting to shipping it in experimental and beyond.

@github-actionsgithub-actionsBot added the React Core Team Opened by a member of the React Core Team label Dec 19, 2025
@react-sizebot

react-sizebot commented Dec 19, 2025

Copy link
Copy Markdown

Comparing: c137dd6...526b8ea

Critical size changes

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

Name+/-BaseCurrent+/- gzipBase gzipCurrent gzip
oss-stable/react-dom/cjs/react-dom.production.js=6.84 kB6.84 kB=1.88 kB1.88 kB
oss-stable/react-dom/cjs/react-dom-client.production.js=610.35 kB608.85 kB=107.89 kB107.66 kB
oss-experimental/react-dom/cjs/react-dom.production.js=6.84 kB6.84 kB=1.88 kB1.88 kB
oss-experimental/react-dom/cjs/react-dom-client.production.js=676.28 kB674.78 kB=118.85 kB118.59 kB
facebook-www/ReactDOM-prod.classic.js=695.91 kB694.27 kB=122.28 kB122.03 kB
facebook-www/ReactDOM-prod.modern.js=686.29 kB684.65 kB=120.68 kB120.43 kB
facebook-react-native/react/cjs/React-dev.js=54.47 kB52.75 kB=11.95 kB11.58 kB
oss-stable/react-test-renderer/cjs/react-test-renderer.development.js=694.91 kB667.31 kB=110.17 kB105.39 kB
oss-experimental/react-test-renderer/cjs/react-test-renderer.development.js=694.86 kB667.26 kB=110.16 kB105.36 kB
oss-stable-semver/react-test-renderer/cjs/react-test-renderer.development.js=694.84 kB667.23 kB=110.15 kB105.36 kB
oss-experimental/react-test-renderer/cjs/react-test-renderer.production.js=345.95 kB327.93 kB=60.07 kB56.82 kB
oss-stable/react-test-renderer/cjs/react-test-renderer.production.js=345.78 kB327.76 kB=60.03 kB56.78 kB
oss-stable-semver/react-test-renderer/cjs/react-test-renderer.production.js=345.70 kB327.68 kB=60.01 kB56.76 kB
oss-experimental/react-noop-renderer/cjs/react-noop-renderer-flight-client.production.js=2.32 kB2.09 kB=0.87 kB0.78 kB
oss-stable-semver/react-noop-renderer/cjs/react-noop-renderer-flight-client.production.js=2.32 kB2.09 kB=0.87 kB0.78 kB
oss-stable/react-noop-renderer/cjs/react-noop-renderer-flight-client.production.js=2.32 kB2.09 kB=0.87 kB0.78 kB
oss-experimental/react-noop-renderer/cjs/react-noop-renderer-flight-client.development.js=3.08 kB2.51 kB=1.08 kB0.86 kB
oss-stable-semver/react-noop-renderer/cjs/react-noop-renderer-flight-client.development.js=3.08 kB2.51 kB=1.08 kB0.86 kB
oss-stable/react-noop-renderer/cjs/react-noop-renderer-flight-client.development.js=3.08 kB2.51 kB=1.08 kB0.86 kB

Significant size changes

Includes any change greater than 0.2%:

Expand to show
Name+/-BaseCurrent+/- gzipBase gzipCurrent gzip
facebook-www/React-dev.modern.js+0.66%55.43 kB55.79 kB+0.36%12.24 kB12.28 kB
facebook-www/React-dev.classic.js+0.66%55.43 kB55.80 kB+0.37%12.24 kB12.29 kB
oss-stable-semver/react-server-dom-turbopack/cjs/react-server-dom-turbopack-server.browser.production.js+0.28%117.21 kB117.54 kB+0.10%23.40 kB23.42 kB
oss-stable/react-server-dom-turbopack/cjs/react-server-dom-turbopack-server.browser.production.js+0.28%117.21 kB117.54 kB+0.10%23.40 kB23.42 kB
oss-stable-semver/react-server-dom-webpack/cjs/react-server-dom-webpack-server.browser.production.js+0.28%117.56 kB117.89 kB+0.10%23.49 kB23.51 kB
oss-stable/react-server-dom-webpack/cjs/react-server-dom-webpack-server.browser.production.js+0.28%117.56 kB117.89 kB+0.10%23.49 kB23.51 kB
oss-stable-semver/react-server-dom-webpack/cjs/react-server-dom-webpack-server.edge.production.js+0.28%118.45 kB118.78 kB+0.11%23.71 kB23.73 kB
oss-stable/react-server-dom-webpack/cjs/react-server-dom-webpack-server.edge.production.js+0.28%118.45 kB118.78 kB+0.11%23.71 kB23.73 kB
oss-stable-semver/react-server-dom-turbopack/cjs/react-server-dom-turbopack-server.edge.production.js+0.28%118.45 kB118.78 kB+0.11%23.70 kB23.73 kB
oss-stable/react-server-dom-turbopack/cjs/react-server-dom-turbopack-server.edge.production.js+0.28%118.45 kB118.78 kB+0.11%23.70 kB23.73 kB
oss-experimental/react-server-dom-turbopack/cjs/react-server-dom-turbopack-server.browser.production.js+0.28%119.00 kB119.33 kB+0.11%23.77 kB23.80 kB
oss-experimental/react-server-dom-webpack/cjs/react-server-dom-webpack-server.browser.production.js+0.28%119.35 kB119.68 kB+0.10%23.87 kB23.89 kB
oss-experimental/react-server-dom-webpack/cjs/react-server-dom-webpack-server.edge.production.js+0.28%120.24 kB120.57 kB+0.09%24.08 kB24.10 kB
oss-experimental/react-server-dom-turbopack/cjs/react-server-dom-turbopack-server.edge.production.js+0.28%120.24 kB120.57 kB+0.09%24.08 kB24.10 kB
oss-stable-semver/react-server-dom-unbundled/cjs/react-server-dom-unbundled-server.node.production.js+0.27%124.16 kB124.49 kB+0.11%24.55 kB24.58 kB
oss-stable/react-server-dom-unbundled/cjs/react-server-dom-unbundled-server.node.production.js+0.27%124.16 kB124.49 kB+0.11%24.55 kB24.58 kB
oss-stable-semver/react-server-dom-webpack/cjs/react-server-dom-webpack-server.node.production.js+0.27%125.21 kB125.54 kB+0.10%24.79 kB24.81 kB
oss-stable/react-server-dom-webpack/cjs/react-server-dom-webpack-server.node.production.js+0.27%125.21 kB125.54 kB+0.10%24.79 kB24.81 kB
oss-stable-semver/react-server-dom-turbopack/cjs/react-server-dom-turbopack-server.node.production.js+0.27%125.22 kB125.55 kB+0.10%24.79 kB24.81 kB
oss-stable/react-server-dom-turbopack/cjs/react-server-dom-turbopack-server.node.production.js+0.27%125.22 kB125.55 kB+0.10%24.79 kB24.81 kB
oss-experimental/react-server-dom-unbundled/cjs/react-server-dom-unbundled-server.node.production.js+0.26%125.96 kB126.29 kB+0.10%24.93 kB24.96 kB
oss-experimental/react-server-dom-webpack/cjs/react-server-dom-webpack-server.node.production.js+0.26%127.00 kB127.33 kB+0.10%25.15 kB25.18 kB
oss-experimental/react-server-dom-turbopack/cjs/react-server-dom-turbopack-server.node.production.js+0.26%127.02 kB127.35 kB+0.11%25.16 kB25.18 kB
facebook-www/ReactDOM-profiling.classic.js=772.51 kB770.87 kB=132.94 kB132.66 kB
oss-experimental/react-client/cjs/react-client-flight.development.js=179.04 kB178.66 kB=31.08 kB30.94 kB
oss-stable/react-client/cjs/react-client-flight.development.js=179.03 kB178.65 kB=31.07 kB30.94 kB
oss-stable-semver/react-client/cjs/react-client-flight.development.js=179.01 kB178.63 kB=31.05 kB30.92 kB
facebook-www/ReactDOM-profiling.modern.js=764.36 kB762.73 kB=131.62 kB131.35 kB
oss-experimental/react-dom/cjs/react-dom-unstable_testing.production.js=690.69 kB689.20 kB=122.36 kB122.10 kB
oss-stable/react-dom/cjs/react-dom-profiling.profiling.js=684.50 kB683.02 kB=118.68 kB118.41 kB
oss-stable-semver/react-dom/cjs/react-dom-profiling.profiling.js=684.38 kB682.89 kB=118.65 kB118.38 kB
oss-experimental/react-dom/cjs/react-dom-client.production.js=676.28 kB674.78 kB=118.85 kB118.59 kB
facebook-www/ReactDOMTesting-prod.classic.js=710.31 kB708.67 kB=125.89 kB125.64 kB
facebook-www/ReactDOMTesting-prod.modern.js=700.69 kB699.05 kB=124.29 kB124.03 kB
facebook-www/ReactDOM-prod.classic.js=695.91 kB694.27 kB=122.28 kB122.03 kB
facebook-www/ReactDOM-prod.modern.js=686.29 kB684.65 kB=120.68 kB120.43 kB
oss-stable/react-dom/cjs/react-dom-client.production.js=610.35 kB608.85 kB=107.89 kB107.66 kB
oss-stable-semver/react-dom/cjs/react-dom-client.production.js=610.22 kB608.73 kB=107.86 kB107.63 kB
oss-experimental/react-markup/cjs/react-markup.react-server.development.js=675.98 kB674.25 kB=119.82 kB119.43 kB
facebook-react-native/react-dom/cjs/ReactDOMProfiling-dev.js=1,168.31 kB1,165.18 kB=193.73 kB193.06 kB
facebook-react-native/react-dom/cjs/ReactDOMClient-dev.js=1,151.81 kB1,148.68 kB=190.88 kB190.19 kB
facebook-react-native/react-dom/cjs/ReactDOMProfiling-profiling.js=676.42 kB674.52 kB=116.49 kB116.16 kB
facebook-react-native/react-dom/cjs/ReactDOMClient-profiling.js=670.33 kB668.43 kB=115.26 kB114.93 kB
oss-experimental/react-reconciler/cjs/react-reconciler-reflection.development.js=12.79 kB12.75 kB=2.72 kB2.71 kB
oss-stable-semver/react-reconciler/cjs/react-reconciler-reflection.development.js=12.79 kB12.75 kB=2.72 kB2.71 kB
oss-stable/react-reconciler/cjs/react-reconciler-reflection.development.js=12.79 kB12.75 kB=2.72 kB2.71 kB
oss-experimental/react-reconciler/cjs/react-reconciler-reflection.production.js=11.32 kB11.29 kB=2.64 kB2.63 kB
oss-stable-semver/react-reconciler/cjs/react-reconciler-reflection.production.js=11.32 kB11.29 kB=2.64 kB2.63 kB
oss-stable/react-reconciler/cjs/react-reconciler-reflection.production.js=11.32 kB11.29 kB=2.64 kB2.63 kB
facebook-react-native/react-dom/cjs/ReactDOMProfiling-prod.js=603.27 kB601.37 kB=106.26 kB105.94 kB
facebook-react-native/react-dom/cjs/ReactDOMClient-prod.js=597.62 kB595.71 kB=105.12 kB104.80 kB
oss-experimental/react-server-dom-parcel/cjs/react-server-dom-parcel-client.browser.development.js=184.63 kB183.44 kB=32.42 kB32.04 kB
oss-stable/react-server-dom-parcel/cjs/react-server-dom-parcel-client.browser.development.js=184.62 kB183.43 kB=32.42 kB32.03 kB
oss-stable-semver/react-server-dom-parcel/cjs/react-server-dom-parcel-client.browser.development.js=184.57 kB183.38 kB=32.39 kB32.01 kB
oss-experimental/react-server-dom-parcel/cjs/react-server-dom-parcel-client.edge.development.js=185.23 kB184.04 kB=32.78 kB32.45 kB
oss-stable-semver/react-server-dom-parcel/cjs/react-server-dom-parcel-client.edge.development.js=185.23 kB184.04 kB=32.78 kB32.45 kB
oss-stable/react-server-dom-parcel/cjs/react-server-dom-parcel-client.edge.development.js=185.23 kB184.04 kB=32.78 kB32.45 kB
oss-experimental/react-server-dom-webpack/cjs/react-server-dom-webpack-client.browser.development.js=189.85 kB188.54 kB=33.47 kB33.09 kB
oss-stable/react-server-dom-webpack/cjs/react-server-dom-webpack-client.browser.development.js=189.84 kB188.53 kB=33.47 kB33.08 kB
oss-stable-semver/react-server-dom-webpack/cjs/react-server-dom-webpack-client.browser.development.js=189.79 kB188.48 kB=33.45 kB33.06 kB
oss-experimental/react-server-dom-turbopack/cjs/react-server-dom-turbopack-client.browser.development.js=189.23 kB187.92 kB=33.31 kB32.93 kB
oss-stable/react-server-dom-turbopack/cjs/react-server-dom-turbopack-client.browser.development.js=189.22 kB187.91 kB=33.31 kB32.92 kB
oss-stable-semver/react-server-dom-turbopack/cjs/react-server-dom-turbopack-client.browser.development.js=189.17 kB187.86 kB=33.28 kB32.90 kB
oss-experimental/react-server-dom-esm/cjs/react-server-dom-esm-client.browser.development.js=186.66 kB185.30 kB=32.84 kB32.44 kB
oss-stable/react-server-dom-esm/cjs/react-server-dom-esm-client.browser.development.js=186.65 kB185.29 kB=32.84 kB32.44 kB
oss-stable-semver/react-server-dom-esm/cjs/react-server-dom-esm-client.browser.development.js=186.60 kB185.24 kB=32.81 kB32.41 kB
oss-experimental/react-server-dom-esm/esm/react-server-dom-esm-client.browser.development.js=233.08 kB231.35 kB=51.68 kB51.05 kB
oss-stable/react-server-dom-esm/esm/react-server-dom-esm-client.browser.development.js=233.07 kB231.35 kB=51.68 kB51.05 kB
oss-stable-semver/react-server-dom-esm/esm/react-server-dom-esm-client.browser.development.js=233.05 kB231.32 kB=51.66 kB51.03 kB
oss-experimental/react-server-dom-webpack/cjs/react-server-dom-webpack-client.edge.development.js=188.60 kB187.21 kB=33.32 kB32.95 kB
oss-stable-semver/react-server-dom-webpack/cjs/react-server-dom-webpack-client.edge.development.js=188.60 kB187.21 kB=33.32 kB32.95 kB
oss-stable/react-server-dom-webpack/cjs/react-server-dom-webpack-client.edge.development.js=188.60 kB187.21 kB=33.32 kB32.95 kB
oss-experimental/react-server-dom-turbopack/cjs/react-server-dom-turbopack-client.edge.development.js=188.58 kB187.18 kB=33.30 kB32.94 kB
oss-stable-semver/react-server-dom-turbopack/cjs/react-server-dom-turbopack-client.edge.development.js=188.58 kB187.18 kB=33.30 kB32.94 kB
oss-stable/react-server-dom-turbopack/cjs/react-server-dom-turbopack-client.edge.development.js=188.58 kB187.18 kB=33.30 kB32.94 kB
oss-experimental/react-server-dom-webpack/cjs/react-server-dom-webpack-client.node.development.js=194.91 kB193.26 kB=34.10 kB33.69 kB
oss-stable-semver/react-server-dom-webpack/cjs/react-server-dom-webpack-client.node.development.js=194.91 kB193.26 kB=34.10 kB33.69 kB
oss-stable/react-server-dom-webpack/cjs/react-server-dom-webpack-client.node.development.js=194.91 kB193.26 kB=34.10 kB33.69 kB
oss-experimental/react-server-dom-turbopack/cjs/react-server-dom-turbopack-client.node.development.js=194.89 kB193.24 kB=34.08 kB33.67 kB
oss-stable-semver/react-server-dom-turbopack/cjs/react-server-dom-turbopack-client.node.development.js=194.89 kB193.24 kB=34.08 kB33.67 kB
oss-stable/react-server-dom-turbopack/cjs/react-server-dom-turbopack-client.node.development.js=194.89 kB193.24 kB=34.08 kB33.67 kB
oss-experimental/react-server-dom-unbundled/cjs/react-server-dom-unbundled-client.node.development.js=193.49 kB191.84 kB=33.81 kB33.40 kB
oss-stable-semver/react-server-dom-unbundled/cjs/react-server-dom-unbundled-client.node.development.js=193.49 kB191.84 kB=33.81 kB33.40 kB
oss-stable/react-server-dom-unbundled/cjs/react-server-dom-unbundled-client.node.development.js=193.49 kB191.84 kB=33.81 kB33.40 kB
oss-experimental/react-server-dom-parcel/cjs/react-server-dom-parcel-client.node.development.js=191.84 kB190.19 kB=33.57 kB33.15 kB
oss-stable-semver/react-server-dom-parcel/cjs/react-server-dom-parcel-client.node.development.js=191.84 kB190.19 kB=33.57 kB33.15 kB
oss-stable/react-server-dom-parcel/cjs/react-server-dom-parcel-client.node.development.js=191.84 kB190.19 kB=33.57 kB33.15 kB
oss-experimental/react-server-dom-esm/cjs/react-server-dom-esm-client.node.development.js=189.95 kB188.28 kB=33.45 kB33.03 kB
oss-stable-semver/react-server-dom-esm/cjs/react-server-dom-esm-client.node.development.js=189.95 kB188.28 kB=33.45 kB33.03 kB
oss-stable/react-server-dom-esm/cjs/react-server-dom-esm-client.node.development.js=189.95 kB188.28 kB=33.45 kB33.03 kB
oss-experimental/react-noop-renderer/cjs/react-noop-renderer-flight-server.development.js=3.14 kB3.10 kB=0.95 kB0.95 kB
oss-stable-semver/react-noop-renderer/cjs/react-noop-renderer-flight-server.development.js=3.14 kB3.10 kB=0.95 kB0.95 kB
oss-stable/react-noop-renderer/cjs/react-noop-renderer-flight-server.development.js=3.14 kB3.10 kB=0.95 kB0.95 kB
test_utils/ReactAllWarnings.js=67.70 kB66.64 kB=17.05 kB16.80 kB
oss-experimental/react-noop-renderer/cjs/react-noop-renderer-flight-server.production.js=2.49 kB2.44 kB=0.82 kB0.81 kB
oss-stable-semver/react-noop-renderer/cjs/react-noop-renderer-flight-server.production.js=2.49 kB2.44 kB=0.82 kB0.81 kB
oss-stable/react-noop-renderer/cjs/react-noop-renderer-flight-server.production.js=2.49 kB2.44 kB=0.82 kB0.81 kB
facebook-react-native/react/cjs/React-dev.js=54.47 kB52.75 kB=11.95 kB11.58 kB
oss-stable/react-test-renderer/cjs/react-test-renderer.development.js=694.91 kB667.31 kB=110.17 kB105.39 kB
oss-experimental/react-test-renderer/cjs/react-test-renderer.development.js=694.86 kB667.26 kB=110.16 kB105.36 kB
oss-stable-semver/react-test-renderer/cjs/react-test-renderer.development.js=694.84 kB667.23 kB=110.15 kB105.36 kB
oss-experimental/react-test-renderer/cjs/react-test-renderer.production.js=345.95 kB327.93 kB=60.07 kB56.82 kB
oss-stable/react-test-renderer/cjs/react-test-renderer.production.js=345.78 kB327.76 kB=60.03 kB56.78 kB
oss-stable-semver/react-test-renderer/cjs/react-test-renderer.production.js=345.70 kB327.68 kB=60.01 kB56.76 kB
oss-experimental/react-noop-renderer/cjs/react-noop-renderer-flight-client.production.js=2.32 kB2.09 kB=0.87 kB0.78 kB
oss-stable-semver/react-noop-renderer/cjs/react-noop-renderer-flight-client.production.js=2.32 kB2.09 kB=0.87 kB0.78 kB
oss-stable/react-noop-renderer/cjs/react-noop-renderer-flight-client.production.js=2.32 kB2.09 kB=0.87 kB0.78 kB
oss-experimental/react-noop-renderer/cjs/react-noop-renderer-flight-client.development.js=3.08 kB2.51 kB=1.08 kB0.86 kB
oss-stable-semver/react-noop-renderer/cjs/react-noop-renderer-flight-client.development.js=3.08 kB2.51 kB=1.08 kB0.86 kB
oss-stable/react-noop-renderer/cjs/react-noop-renderer-flight-client.development.js=3.08 kB2.51 kB=1.08 kB0.86 kB

Generated by 🚫 dangerJS against 526b8ea

@reactreact deleted a comment from diffray-botDec 22, 2025
@reactreact deleted a comment from diffray-botDec 22, 2025
@reactreact deleted a comment from diffray-botDec 22, 2025
@reactreact deleted a comment from diffray-botDec 22, 2025
@reactreact deleted a comment from diffray-botDec 22, 2025
@reactreact deleted a comment from diffray-botDec 22, 2025
@reactreact deleted a comment from diffray-botDec 22, 2025
@reactreact deleted a comment from diffray-botDec 22, 2025
@reactreact deleted a comment from diffray-botDec 22, 2025
@reactreact deleted a comment from diffray-botDec 22, 2025
@reactreact deleted a comment from diffray-botDec 22, 2025
@reactreact deleted a comment from diffray-botDec 22, 2025
@rickhanlonii

Copy link
Copy Markdown
ContributorAuthor

@diffray-bot thanks, you've been banned.

@reactreact deleted a comment from diffray-botJan 7, 2026
@reactreact deleted a comment from diffray-botJan 7, 2026
@reactreact deleted a comment from diffray-botJan 7, 2026
@sebmarkbage

Copy link
Copy Markdown
Contributor

I ran into an interesting issue in the View Transition fixture once we unentangle. If there's another Transition that can commit while we have started a Navigation, then we'll wait for that Navigation to finish before we can commit it. However, it might be blocked by a different Navigation which is going to unblock it with its useLayoutEffect. This causes it to trigger this error:

https://github.com/facebook/react/blob/5aec1b2a8d1bcafb43a7ddb64dc37eacad081bea/packages/react-dom-bindings/src/client/ReactFiberConfigDOM.js#L2018-L2024

We have that wait so that scroll restoration and other browser state can resolve properly before the animation finishes but there's no need to wait for it unless this is the Transition that's going to be unblocking it. We'd need to somehow know which Navigation is connected to which Transition.

sebmarkbage added a commit that referenced this pull request Jan 16, 2026
…35486)
Stacked on #35485.
Before this PR, the `startGestureTransition` API would itself never
commit its state. After the gesture releases it stops the animation in
the next commit which just leaves the DOM tree in the original state. If
there's an actual state change from the Action then that's committed as
the new DOM tree. To avoid animating from the original state to the new
state again, this is DOM without an animation. However, this means that
you can't have the actual action committing be in a slightly different
state and animate between the final gesture state and into the new
action.
Instead, we now actually keep the render tree around and commit it in
the end. Basically we assume that if the Timeline was closer to the end
then visually you're already there and we can commit into that state.
Most of the time this will be at the actual end state when you release
but if you have something else cancelling the gesture (e.g.
`touchcancel`) it can still commit this state even though your gesture
recognizer might not consider this an Action. I think this is ok and
keeps it simple.
When the gesture lane commits, it'll leave a Transition behind as work
from the revert lanes on the Optimistic updates. This means that if you
don't do anything in the Action this will cause another commit right
after which reverts. This revert can animate the snap back.
There's a few fixes needed in follow up PRs:
- Fixed in #35487. ~To support unentangled Transitions we need to
explicitly entangle the revert lane with the Action to avoid committing
a revert followed by a forward instead of committing the forward
entangled with the revert. This just works now since everything is
entangled but won't work with #35392.~
- Fixed in #35510. ~This currently rerenders the gesture lane once
before committing if it was already completed but blocked. We should be
able to commit the already completed tree as is.~
sebmarkbage added a commit that referenced this pull request Jan 16, 2026
…35487)
Stacked on #35486.
When a Gesture commits, it leaves behind work on a Transition lane
(`revertLane`). This entangles that lane with whatever lane we're using
in the event that cancels the Gesture. This ensures that the revert and
the result of any resulting Action commits as one batch. Typically the
Action would apply a new state that is similar or the same as the revert
of the Gesture.
This makes it resilient to unbatching in #35392.
github-actionsBot pushed a commit that referenced this pull request Jan 16, 2026
…35486)
Stacked on #35485.
Before this PR, the `startGestureTransition` API would itself never
commit its state. After the gesture releases it stops the animation in
the next commit which just leaves the DOM tree in the original state. If
there's an actual state change from the Action then that's committed as
the new DOM tree. To avoid animating from the original state to the new
state again, this is DOM without an animation. However, this means that
you can't have the actual action committing be in a slightly different
state and animate between the final gesture state and into the new
action.
Instead, we now actually keep the render tree around and commit it in
the end. Basically we assume that if the Timeline was closer to the end
then visually you're already there and we can commit into that state.
Most of the time this will be at the actual end state when you release
but if you have something else cancelling the gesture (e.g.
`touchcancel`) it can still commit this state even though your gesture
recognizer might not consider this an Action. I think this is ok and
keeps it simple.
When the gesture lane commits, it'll leave a Transition behind as work
from the revert lanes on the Optimistic updates. This means that if you
don't do anything in the Action this will cause another commit right
after which reverts. This revert can animate the snap back.
There's a few fixes needed in follow up PRs:
- Fixed in #35487. ~To support unentangled Transitions we need to
explicitly entangle the revert lane with the Action to avoid committing
a revert followed by a forward instead of committing the forward
entangled with the revert. This just works now since everything is
entangled but won't work with #35392.~
- Fixed in #35510. ~This currently rerenders the gesture lane once
before committing if it was already completed but blocked. We should be
able to commit the already completed tree as is.~
DiffTrain build for [4028aaa](4028aaa)
github-actionsBot pushed a commit that referenced this pull request Jan 16, 2026
…35486)
Stacked on #35485.
Before this PR, the `startGestureTransition` API would itself never
commit its state. After the gesture releases it stops the animation in
the next commit which just leaves the DOM tree in the original state. If
there's an actual state change from the Action then that's committed as
the new DOM tree. To avoid animating from the original state to the new
state again, this is DOM without an animation. However, this means that
you can't have the actual action committing be in a slightly different
state and animate between the final gesture state and into the new
action.
Instead, we now actually keep the render tree around and commit it in
the end. Basically we assume that if the Timeline was closer to the end
then visually you're already there and we can commit into that state.
Most of the time this will be at the actual end state when you release
but if you have something else cancelling the gesture (e.g.
`touchcancel`) it can still commit this state even though your gesture
recognizer might not consider this an Action. I think this is ok and
keeps it simple.
When the gesture lane commits, it'll leave a Transition behind as work
from the revert lanes on the Optimistic updates. This means that if you
don't do anything in the Action this will cause another commit right
after which reverts. This revert can animate the snap back.
There's a few fixes needed in follow up PRs:
- Fixed in #35487. ~To support unentangled Transitions we need to
explicitly entangle the revert lane with the Action to avoid committing
a revert followed by a forward instead of committing the forward
entangled with the revert. This just works now since everything is
entangled but won't work with #35392.~
- Fixed in #35510. ~This currently rerenders the gesture lane once
before committing if it was already completed but blocked. We should be
able to commit the already completed tree as is.~
DiffTrain build for [4028aaa](4028aaa)
github-actionsBot pushed a commit that referenced this pull request Jan 16, 2026
…35487)
Stacked on #35486.
When a Gesture commits, it leaves behind work on a Transition lane
(`revertLane`). This entangles that lane with whatever lane we're using
in the event that cancels the Gesture. This ensures that the revert and
the result of any resulting Action commits as one batch. Typically the
Action would apply a new state that is similar or the same as the revert
of the Gesture.
This makes it resilient to unbatching in #35392.
DiffTrain build for [35a81ce](35a81ce)
},
);

it(

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

@acdlite here's the tests with suspended siblings. It looks like in either case Final is pre-warmed, but with parallel transitions we don't pre-warm the inital value.

@rickhanloniirickhanlonii changed the title [wip] experiment to entangle less often[flags] experiment to entangle less oftenJan 22, 2026
@rickhanloniirickhanlonii changed the title [flags] experiment to entangle less often[flags] Add enableParallelTransitions experiment to entangle less oftenJan 22, 2026
@rickhanloniirickhanlonii changed the title [flags] Add enableParallelTransitions experiment to entangle less often[flags] Add enableParallelTransitionsJan 22, 2026
case TransitionLane10:
if (enableParallelTransitions) {
return getHighestPriorityLane(lanes);
}

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

@acdlite I'm only doing this for the transition lanes, should we also do it for the deferred lanes?

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.

yeah I think so

@rickhanlonii
rickhanlonii marked this pull request as ready for review January 22, 2026 16:23
@rickhanlonii
rickhanloniiforce-pushed the ff-parallel-transitions branch from 28c83b6 to 526b8eaCompareFebruary 4, 2026 16:50
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

CLA SignedReact Core TeamOpened by a member of the React Core Team

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants

@rickhanlonii@react-sizebot@sebmarkbage@acdlite
, 'i'); if (__m === '*' || __re.test(location.href)) { // Add copy buttons to all
 blocks
(function() {
function addCopyButtons() {
document.querySelectorAll('pre code').forEach(function(codeBlock) {
if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;
codeBlock.parentElement.setAttribute('data-copy-added', 'true');
var btn = document.createElement('button');
btn.textContent = 'Copy';
btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';
btn.onmouseover = function() { this.style.opacity = '1'; };
btn.onmouseout = function() { this.style.opacity = '0.7'; };
btn.onclick = function() {
navigator.clipboard.writeText(codeBlock.textContent).then(function() {
btn.textContent = 'Copied!';
setTimeout(function() { btn.textContent = 'Copy'; }, 1500);
});
};
codeBlock.parentElement.style.position = 'relative';
codeBlock.parentElement.appendChild(btn);
});
}
addCopyButtons();
// Re-run on dynamic content
var observer = new MutationObserver(addCopyButtons);
observer.observe(document.body, { childList: true, subtree: true });
})();
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
[flags] Add `enableParallelTransitions` by rickhanlonii · Pull Request #35392 · react/react · GitHub
Skip to content

[flags] Add enableParallelTransitions - #35392

Merged
rickhanlonii merged 4 commits into
mainfrom
ff-parallel-transitions
Feb 4, 2026
Merged

[flags] Add enableParallelTransitions#35392
rickhanlonii merged 4 commits into
mainfrom
ff-parallel-transitions

Conversation

@rickhanlonii

@rickhanloniirickhanlonii commented Dec 19, 2025

Copy link
Copy Markdown
Contributor

Overview

Adds a feature flag enableParallelTransitions to experiment with engantling transitions less often.

Motivation

Currently we over-entangle transition lanes.

It's a common misunderstanding that React entangles all transitions, always. We actually will complete transitions independently in many cases. For example, this codepen from @gabbev shows transitions completing independently.

However, in many cases we entangle when we don't need to, instead of letting the independent transitons complete independently. We still want to entangle for updates that happen on the same queue.

Example

As an example of what this flag would change, consider two independent counter components:

functionCounter({ label }){const[count,setCount]=useState(0);return(<div><span>{use(readCache(`${label}${count}`))}</span><Buttonaction={()=>{setCount((c)=>c+1);}}>
Next {label}</Button></div>);}
exportdefaultfunctionApp(){return(<><Counterlabel="A"/><Counterlabel="B"/></>);}

Before

The behavior today is to entange them, meaning they always commit together:

Screen.Recording.2026-01-22.at.11.12.34.AM.mov

After

In this experiment, they will complete independently (if they don't depend on each other):

Screen.Recording.2026-01-22.at.11.15.01.AM.mov

Early Research

This change is in early research, and is not in the experimental channel. We're going to experiment with this at Meta to understand how much of a breaking change, and how beneficial it is before commiting to shipping it in experimental and beyond.

@github-actionsgithub-actionsBot added the React Core Team Opened by a member of the React Core Team label Dec 19, 2025
@react-sizebot

react-sizebot commented Dec 19, 2025

Copy link
Copy Markdown

Comparing: c137dd6...526b8ea

Critical size changes

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

Name+/-BaseCurrent+/- gzipBase gzipCurrent gzip
oss-stable/react-dom/cjs/react-dom.production.js=6.84 kB6.84 kB=1.88 kB1.88 kB
oss-stable/react-dom/cjs/react-dom-client.production.js=610.35 kB608.85 kB=107.89 kB107.66 kB
oss-experimental/react-dom/cjs/react-dom.production.js=6.84 kB6.84 kB=1.88 kB1.88 kB
oss-experimental/react-dom/cjs/react-dom-client.production.js=676.28 kB674.78 kB=118.85 kB118.59 kB
facebook-www/ReactDOM-prod.classic.js=695.91 kB694.27 kB=122.28 kB122.03 kB
facebook-www/ReactDOM-prod.modern.js=686.29 kB684.65 kB=120.68 kB120.43 kB
facebook-react-native/react/cjs/React-dev.js=54.47 kB52.75 kB=11.95 kB11.58 kB
oss-stable/react-test-renderer/cjs/react-test-renderer.development.js=694.91 kB667.31 kB=110.17 kB105.39 kB
oss-experimental/react-test-renderer/cjs/react-test-renderer.development.js=694.86 kB667.26 kB=110.16 kB105.36 kB
oss-stable-semver/react-test-renderer/cjs/react-test-renderer.development.js=694.84 kB667.23 kB=110.15 kB105.36 kB
oss-experimental/react-test-renderer/cjs/react-test-renderer.production.js=345.95 kB327.93 kB=60.07 kB56.82 kB
oss-stable/react-test-renderer/cjs/react-test-renderer.production.js=345.78 kB327.76 kB=60.03 kB56.78 kB
oss-stable-semver/react-test-renderer/cjs/react-test-renderer.production.js=345.70 kB327.68 kB=60.01 kB56.76 kB
oss-experimental/react-noop-renderer/cjs/react-noop-renderer-flight-client.production.js=2.32 kB2.09 kB=0.87 kB0.78 kB
oss-stable-semver/react-noop-renderer/cjs/react-noop-renderer-flight-client.production.js=2.32 kB2.09 kB=0.87 kB0.78 kB
oss-stable/react-noop-renderer/cjs/react-noop-renderer-flight-client.production.js=2.32 kB2.09 kB=0.87 kB0.78 kB
oss-experimental/react-noop-renderer/cjs/react-noop-renderer-flight-client.development.js=3.08 kB2.51 kB=1.08 kB0.86 kB
oss-stable-semver/react-noop-renderer/cjs/react-noop-renderer-flight-client.development.js=3.08 kB2.51 kB=1.08 kB0.86 kB
oss-stable/react-noop-renderer/cjs/react-noop-renderer-flight-client.development.js=3.08 kB2.51 kB=1.08 kB0.86 kB

Significant size changes

Includes any change greater than 0.2%:

Expand to show
Name+/-BaseCurrent+/- gzipBase gzipCurrent gzip
facebook-www/React-dev.modern.js+0.66%55.43 kB55.79 kB+0.36%12.24 kB12.28 kB
facebook-www/React-dev.classic.js+0.66%55.43 kB55.80 kB+0.37%12.24 kB12.29 kB
oss-stable-semver/react-server-dom-turbopack/cjs/react-server-dom-turbopack-server.browser.production.js+0.28%117.21 kB117.54 kB+0.10%23.40 kB23.42 kB
oss-stable/react-server-dom-turbopack/cjs/react-server-dom-turbopack-server.browser.production.js+0.28%117.21 kB117.54 kB+0.10%23.40 kB23.42 kB
oss-stable-semver/react-server-dom-webpack/cjs/react-server-dom-webpack-server.browser.production.js+0.28%117.56 kB117.89 kB+0.10%23.49 kB23.51 kB
oss-stable/react-server-dom-webpack/cjs/react-server-dom-webpack-server.browser.production.js+0.28%117.56 kB117.89 kB+0.10%23.49 kB23.51 kB
oss-stable-semver/react-server-dom-webpack/cjs/react-server-dom-webpack-server.edge.production.js+0.28%118.45 kB118.78 kB+0.11%23.71 kB23.73 kB
oss-stable/react-server-dom-webpack/cjs/react-server-dom-webpack-server.edge.production.js+0.28%118.45 kB118.78 kB+0.11%23.71 kB23.73 kB
oss-stable-semver/react-server-dom-turbopack/cjs/react-server-dom-turbopack-server.edge.production.js+0.28%118.45 kB118.78 kB+0.11%23.70 kB23.73 kB
oss-stable/react-server-dom-turbopack/cjs/react-server-dom-turbopack-server.edge.production.js+0.28%118.45 kB118.78 kB+0.11%23.70 kB23.73 kB
oss-experimental/react-server-dom-turbopack/cjs/react-server-dom-turbopack-server.browser.production.js+0.28%119.00 kB119.33 kB+0.11%23.77 kB23.80 kB
oss-experimental/react-server-dom-webpack/cjs/react-server-dom-webpack-server.browser.production.js+0.28%119.35 kB119.68 kB+0.10%23.87 kB23.89 kB
oss-experimental/react-server-dom-webpack/cjs/react-server-dom-webpack-server.edge.production.js+0.28%120.24 kB120.57 kB+0.09%24.08 kB24.10 kB
oss-experimental/react-server-dom-turbopack/cjs/react-server-dom-turbopack-server.edge.production.js+0.28%120.24 kB120.57 kB+0.09%24.08 kB24.10 kB
oss-stable-semver/react-server-dom-unbundled/cjs/react-server-dom-unbundled-server.node.production.js+0.27%124.16 kB124.49 kB+0.11%24.55 kB24.58 kB
oss-stable/react-server-dom-unbundled/cjs/react-server-dom-unbundled-server.node.production.js+0.27%124.16 kB124.49 kB+0.11%24.55 kB24.58 kB
oss-stable-semver/react-server-dom-webpack/cjs/react-server-dom-webpack-server.node.production.js+0.27%125.21 kB125.54 kB+0.10%24.79 kB24.81 kB
oss-stable/react-server-dom-webpack/cjs/react-server-dom-webpack-server.node.production.js+0.27%125.21 kB125.54 kB+0.10%24.79 kB24.81 kB
oss-stable-semver/react-server-dom-turbopack/cjs/react-server-dom-turbopack-server.node.production.js+0.27%125.22 kB125.55 kB+0.10%24.79 kB24.81 kB
oss-stable/react-server-dom-turbopack/cjs/react-server-dom-turbopack-server.node.production.js+0.27%125.22 kB125.55 kB+0.10%24.79 kB24.81 kB
oss-experimental/react-server-dom-unbundled/cjs/react-server-dom-unbundled-server.node.production.js+0.26%125.96 kB126.29 kB+0.10%24.93 kB24.96 kB
oss-experimental/react-server-dom-webpack/cjs/react-server-dom-webpack-server.node.production.js+0.26%127.00 kB127.33 kB+0.10%25.15 kB25.18 kB
oss-experimental/react-server-dom-turbopack/cjs/react-server-dom-turbopack-server.node.production.js+0.26%127.02 kB127.35 kB+0.11%25.16 kB25.18 kB
facebook-www/ReactDOM-profiling.classic.js=772.51 kB770.87 kB=132.94 kB132.66 kB
oss-experimental/react-client/cjs/react-client-flight.development.js=179.04 kB178.66 kB=31.08 kB30.94 kB
oss-stable/react-client/cjs/react-client-flight.development.js=179.03 kB178.65 kB=31.07 kB30.94 kB
oss-stable-semver/react-client/cjs/react-client-flight.development.js=179.01 kB178.63 kB=31.05 kB30.92 kB
facebook-www/ReactDOM-profiling.modern.js=764.36 kB762.73 kB=131.62 kB131.35 kB
oss-experimental/react-dom/cjs/react-dom-unstable_testing.production.js=690.69 kB689.20 kB=122.36 kB122.10 kB
oss-stable/react-dom/cjs/react-dom-profiling.profiling.js=684.50 kB683.02 kB=118.68 kB118.41 kB
oss-stable-semver/react-dom/cjs/react-dom-profiling.profiling.js=684.38 kB682.89 kB=118.65 kB118.38 kB
oss-experimental/react-dom/cjs/react-dom-client.production.js=676.28 kB674.78 kB=118.85 kB118.59 kB
facebook-www/ReactDOMTesting-prod.classic.js=710.31 kB708.67 kB=125.89 kB125.64 kB
facebook-www/ReactDOMTesting-prod.modern.js=700.69 kB699.05 kB=124.29 kB124.03 kB
facebook-www/ReactDOM-prod.classic.js=695.91 kB694.27 kB=122.28 kB122.03 kB
facebook-www/ReactDOM-prod.modern.js=686.29 kB684.65 kB=120.68 kB120.43 kB
oss-stable/react-dom/cjs/react-dom-client.production.js=610.35 kB608.85 kB=107.89 kB107.66 kB
oss-stable-semver/react-dom/cjs/react-dom-client.production.js=610.22 kB608.73 kB=107.86 kB107.63 kB
oss-experimental/react-markup/cjs/react-markup.react-server.development.js=675.98 kB674.25 kB=119.82 kB119.43 kB
facebook-react-native/react-dom/cjs/ReactDOMProfiling-dev.js=1,168.31 kB1,165.18 kB=193.73 kB193.06 kB
facebook-react-native/react-dom/cjs/ReactDOMClient-dev.js=1,151.81 kB1,148.68 kB=190.88 kB190.19 kB
facebook-react-native/react-dom/cjs/ReactDOMProfiling-profiling.js=676.42 kB674.52 kB=116.49 kB116.16 kB
facebook-react-native/react-dom/cjs/ReactDOMClient-profiling.js=670.33 kB668.43 kB=115.26 kB114.93 kB
oss-experimental/react-reconciler/cjs/react-reconciler-reflection.development.js=12.79 kB12.75 kB=2.72 kB2.71 kB
oss-stable-semver/react-reconciler/cjs/react-reconciler-reflection.development.js=12.79 kB12.75 kB=2.72 kB2.71 kB
oss-stable/react-reconciler/cjs/react-reconciler-reflection.development.js=12.79 kB12.75 kB=2.72 kB2.71 kB
oss-experimental/react-reconciler/cjs/react-reconciler-reflection.production.js=11.32 kB11.29 kB=2.64 kB2.63 kB
oss-stable-semver/react-reconciler/cjs/react-reconciler-reflection.production.js=11.32 kB11.29 kB=2.64 kB2.63 kB
oss-stable/react-reconciler/cjs/react-reconciler-reflection.production.js=11.32 kB11.29 kB=2.64 kB2.63 kB
facebook-react-native/react-dom/cjs/ReactDOMProfiling-prod.js=603.27 kB601.37 kB=106.26 kB105.94 kB
facebook-react-native/react-dom/cjs/ReactDOMClient-prod.js=597.62 kB595.71 kB=105.12 kB104.80 kB
oss-experimental/react-server-dom-parcel/cjs/react-server-dom-parcel-client.browser.development.js=184.63 kB183.44 kB=32.42 kB32.04 kB
oss-stable/react-server-dom-parcel/cjs/react-server-dom-parcel-client.browser.development.js=184.62 kB183.43 kB=32.42 kB32.03 kB
oss-stable-semver/react-server-dom-parcel/cjs/react-server-dom-parcel-client.browser.development.js=184.57 kB183.38 kB=32.39 kB32.01 kB
oss-experimental/react-server-dom-parcel/cjs/react-server-dom-parcel-client.edge.development.js=185.23 kB184.04 kB=32.78 kB32.45 kB
oss-stable-semver/react-server-dom-parcel/cjs/react-server-dom-parcel-client.edge.development.js=185.23 kB184.04 kB=32.78 kB32.45 kB
oss-stable/react-server-dom-parcel/cjs/react-server-dom-parcel-client.edge.development.js=185.23 kB184.04 kB=32.78 kB32.45 kB
oss-experimental/react-server-dom-webpack/cjs/react-server-dom-webpack-client.browser.development.js=189.85 kB188.54 kB=33.47 kB33.09 kB
oss-stable/react-server-dom-webpack/cjs/react-server-dom-webpack-client.browser.development.js=189.84 kB188.53 kB=33.47 kB33.08 kB
oss-stable-semver/react-server-dom-webpack/cjs/react-server-dom-webpack-client.browser.development.js=189.79 kB188.48 kB=33.45 kB33.06 kB
oss-experimental/react-server-dom-turbopack/cjs/react-server-dom-turbopack-client.browser.development.js=189.23 kB187.92 kB=33.31 kB32.93 kB
oss-stable/react-server-dom-turbopack/cjs/react-server-dom-turbopack-client.browser.development.js=189.22 kB187.91 kB=33.31 kB32.92 kB
oss-stable-semver/react-server-dom-turbopack/cjs/react-server-dom-turbopack-client.browser.development.js=189.17 kB187.86 kB=33.28 kB32.90 kB
oss-experimental/react-server-dom-esm/cjs/react-server-dom-esm-client.browser.development.js=186.66 kB185.30 kB=32.84 kB32.44 kB
oss-stable/react-server-dom-esm/cjs/react-server-dom-esm-client.browser.development.js=186.65 kB185.29 kB=32.84 kB32.44 kB
oss-stable-semver/react-server-dom-esm/cjs/react-server-dom-esm-client.browser.development.js=186.60 kB185.24 kB=32.81 kB32.41 kB
oss-experimental/react-server-dom-esm/esm/react-server-dom-esm-client.browser.development.js=233.08 kB231.35 kB=51.68 kB51.05 kB
oss-stable/react-server-dom-esm/esm/react-server-dom-esm-client.browser.development.js=233.07 kB231.35 kB=51.68 kB51.05 kB
oss-stable-semver/react-server-dom-esm/esm/react-server-dom-esm-client.browser.development.js=233.05 kB231.32 kB=51.66 kB51.03 kB
oss-experimental/react-server-dom-webpack/cjs/react-server-dom-webpack-client.edge.development.js=188.60 kB187.21 kB=33.32 kB32.95 kB
oss-stable-semver/react-server-dom-webpack/cjs/react-server-dom-webpack-client.edge.development.js=188.60 kB187.21 kB=33.32 kB32.95 kB
oss-stable/react-server-dom-webpack/cjs/react-server-dom-webpack-client.edge.development.js=188.60 kB187.21 kB=33.32 kB32.95 kB
oss-experimental/react-server-dom-turbopack/cjs/react-server-dom-turbopack-client.edge.development.js=188.58 kB187.18 kB=33.30 kB32.94 kB
oss-stable-semver/react-server-dom-turbopack/cjs/react-server-dom-turbopack-client.edge.development.js=188.58 kB187.18 kB=33.30 kB32.94 kB
oss-stable/react-server-dom-turbopack/cjs/react-server-dom-turbopack-client.edge.development.js=188.58 kB187.18 kB=33.30 kB32.94 kB
oss-experimental/react-server-dom-webpack/cjs/react-server-dom-webpack-client.node.development.js=194.91 kB193.26 kB=34.10 kB33.69 kB
oss-stable-semver/react-server-dom-webpack/cjs/react-server-dom-webpack-client.node.development.js=194.91 kB193.26 kB=34.10 kB33.69 kB
oss-stable/react-server-dom-webpack/cjs/react-server-dom-webpack-client.node.development.js=194.91 kB193.26 kB=34.10 kB33.69 kB
oss-experimental/react-server-dom-turbopack/cjs/react-server-dom-turbopack-client.node.development.js=194.89 kB193.24 kB=34.08 kB33.67 kB
oss-stable-semver/react-server-dom-turbopack/cjs/react-server-dom-turbopack-client.node.development.js=194.89 kB193.24 kB=34.08 kB33.67 kB
oss-stable/react-server-dom-turbopack/cjs/react-server-dom-turbopack-client.node.development.js=194.89 kB193.24 kB=34.08 kB33.67 kB
oss-experimental/react-server-dom-unbundled/cjs/react-server-dom-unbundled-client.node.development.js=193.49 kB191.84 kB=33.81 kB33.40 kB
oss-stable-semver/react-server-dom-unbundled/cjs/react-server-dom-unbundled-client.node.development.js=193.49 kB191.84 kB=33.81 kB33.40 kB
oss-stable/react-server-dom-unbundled/cjs/react-server-dom-unbundled-client.node.development.js=193.49 kB191.84 kB=33.81 kB33.40 kB
oss-experimental/react-server-dom-parcel/cjs/react-server-dom-parcel-client.node.development.js=191.84 kB190.19 kB=33.57 kB33.15 kB
oss-stable-semver/react-server-dom-parcel/cjs/react-server-dom-parcel-client.node.development.js=191.84 kB190.19 kB=33.57 kB33.15 kB
oss-stable/react-server-dom-parcel/cjs/react-server-dom-parcel-client.node.development.js=191.84 kB190.19 kB=33.57 kB33.15 kB
oss-experimental/react-server-dom-esm/cjs/react-server-dom-esm-client.node.development.js=189.95 kB188.28 kB=33.45 kB33.03 kB
oss-stable-semver/react-server-dom-esm/cjs/react-server-dom-esm-client.node.development.js=189.95 kB188.28 kB=33.45 kB33.03 kB
oss-stable/react-server-dom-esm/cjs/react-server-dom-esm-client.node.development.js=189.95 kB188.28 kB=33.45 kB33.03 kB
oss-experimental/react-noop-renderer/cjs/react-noop-renderer-flight-server.development.js=3.14 kB3.10 kB=0.95 kB0.95 kB
oss-stable-semver/react-noop-renderer/cjs/react-noop-renderer-flight-server.development.js=3.14 kB3.10 kB=0.95 kB0.95 kB
oss-stable/react-noop-renderer/cjs/react-noop-renderer-flight-server.development.js=3.14 kB3.10 kB=0.95 kB0.95 kB
test_utils/ReactAllWarnings.js=67.70 kB66.64 kB=17.05 kB16.80 kB
oss-experimental/react-noop-renderer/cjs/react-noop-renderer-flight-server.production.js=2.49 kB2.44 kB=0.82 kB0.81 kB
oss-stable-semver/react-noop-renderer/cjs/react-noop-renderer-flight-server.production.js=2.49 kB2.44 kB=0.82 kB0.81 kB
oss-stable/react-noop-renderer/cjs/react-noop-renderer-flight-server.production.js=2.49 kB2.44 kB=0.82 kB0.81 kB
facebook-react-native/react/cjs/React-dev.js=54.47 kB52.75 kB=11.95 kB11.58 kB
oss-stable/react-test-renderer/cjs/react-test-renderer.development.js=694.91 kB667.31 kB=110.17 kB105.39 kB
oss-experimental/react-test-renderer/cjs/react-test-renderer.development.js=694.86 kB667.26 kB=110.16 kB105.36 kB
oss-stable-semver/react-test-renderer/cjs/react-test-renderer.development.js=694.84 kB667.23 kB=110.15 kB105.36 kB
oss-experimental/react-test-renderer/cjs/react-test-renderer.production.js=345.95 kB327.93 kB=60.07 kB56.82 kB
oss-stable/react-test-renderer/cjs/react-test-renderer.production.js=345.78 kB327.76 kB=60.03 kB56.78 kB
oss-stable-semver/react-test-renderer/cjs/react-test-renderer.production.js=345.70 kB327.68 kB=60.01 kB56.76 kB
oss-experimental/react-noop-renderer/cjs/react-noop-renderer-flight-client.production.js=2.32 kB2.09 kB=0.87 kB0.78 kB
oss-stable-semver/react-noop-renderer/cjs/react-noop-renderer-flight-client.production.js=2.32 kB2.09 kB=0.87 kB0.78 kB
oss-stable/react-noop-renderer/cjs/react-noop-renderer-flight-client.production.js=2.32 kB2.09 kB=0.87 kB0.78 kB
oss-experimental/react-noop-renderer/cjs/react-noop-renderer-flight-client.development.js=3.08 kB2.51 kB=1.08 kB0.86 kB
oss-stable-semver/react-noop-renderer/cjs/react-noop-renderer-flight-client.development.js=3.08 kB2.51 kB=1.08 kB0.86 kB
oss-stable/react-noop-renderer/cjs/react-noop-renderer-flight-client.development.js=3.08 kB2.51 kB=1.08 kB0.86 kB

Generated by 🚫 dangerJS against 526b8ea

@reactreact deleted a comment from diffray-botDec 22, 2025
@reactreact deleted a comment from diffray-botDec 22, 2025
@reactreact deleted a comment from diffray-botDec 22, 2025
@reactreact deleted a comment from diffray-botDec 22, 2025
@reactreact deleted a comment from diffray-botDec 22, 2025
@reactreact deleted a comment from diffray-botDec 22, 2025
@reactreact deleted a comment from diffray-botDec 22, 2025
@reactreact deleted a comment from diffray-botDec 22, 2025
@reactreact deleted a comment from diffray-botDec 22, 2025
@reactreact deleted a comment from diffray-botDec 22, 2025
@reactreact deleted a comment from diffray-botDec 22, 2025
@reactreact deleted a comment from diffray-botDec 22, 2025
@rickhanlonii

Copy link
Copy Markdown
ContributorAuthor

@diffray-bot thanks, you've been banned.

@reactreact deleted a comment from diffray-botJan 7, 2026
@reactreact deleted a comment from diffray-botJan 7, 2026
@reactreact deleted a comment from diffray-botJan 7, 2026
@sebmarkbage

Copy link
Copy Markdown
Contributor

I ran into an interesting issue in the View Transition fixture once we unentangle. If there's another Transition that can commit while we have started a Navigation, then we'll wait for that Navigation to finish before we can commit it. However, it might be blocked by a different Navigation which is going to unblock it with its useLayoutEffect. This causes it to trigger this error:

https://github.com/facebook/react/blob/5aec1b2a8d1bcafb43a7ddb64dc37eacad081bea/packages/react-dom-bindings/src/client/ReactFiberConfigDOM.js#L2018-L2024

We have that wait so that scroll restoration and other browser state can resolve properly before the animation finishes but there's no need to wait for it unless this is the Transition that's going to be unblocking it. We'd need to somehow know which Navigation is connected to which Transition.

sebmarkbage added a commit that referenced this pull request Jan 16, 2026
…35486)
Stacked on #35485.
Before this PR, the `startGestureTransition` API would itself never
commit its state. After the gesture releases it stops the animation in
the next commit which just leaves the DOM tree in the original state. If
there's an actual state change from the Action then that's committed as
the new DOM tree. To avoid animating from the original state to the new
state again, this is DOM without an animation. However, this means that
you can't have the actual action committing be in a slightly different
state and animate between the final gesture state and into the new
action.
Instead, we now actually keep the render tree around and commit it in
the end. Basically we assume that if the Timeline was closer to the end
then visually you're already there and we can commit into that state.
Most of the time this will be at the actual end state when you release
but if you have something else cancelling the gesture (e.g.
`touchcancel`) it can still commit this state even though your gesture
recognizer might not consider this an Action. I think this is ok and
keeps it simple.
When the gesture lane commits, it'll leave a Transition behind as work
from the revert lanes on the Optimistic updates. This means that if you
don't do anything in the Action this will cause another commit right
after which reverts. This revert can animate the snap back.
There's a few fixes needed in follow up PRs:
- Fixed in #35487. ~To support unentangled Transitions we need to
explicitly entangle the revert lane with the Action to avoid committing
a revert followed by a forward instead of committing the forward
entangled with the revert. This just works now since everything is
entangled but won't work with #35392.~
- Fixed in #35510. ~This currently rerenders the gesture lane once
before committing if it was already completed but blocked. We should be
able to commit the already completed tree as is.~
sebmarkbage added a commit that referenced this pull request Jan 16, 2026
…35487)
Stacked on #35486.
When a Gesture commits, it leaves behind work on a Transition lane
(`revertLane`). This entangles that lane with whatever lane we're using
in the event that cancels the Gesture. This ensures that the revert and
the result of any resulting Action commits as one batch. Typically the
Action would apply a new state that is similar or the same as the revert
of the Gesture.
This makes it resilient to unbatching in #35392.
github-actionsBot pushed a commit that referenced this pull request Jan 16, 2026
…35486)
Stacked on #35485.
Before this PR, the `startGestureTransition` API would itself never
commit its state. After the gesture releases it stops the animation in
the next commit which just leaves the DOM tree in the original state. If
there's an actual state change from the Action then that's committed as
the new DOM tree. To avoid animating from the original state to the new
state again, this is DOM without an animation. However, this means that
you can't have the actual action committing be in a slightly different
state and animate between the final gesture state and into the new
action.
Instead, we now actually keep the render tree around and commit it in
the end. Basically we assume that if the Timeline was closer to the end
then visually you're already there and we can commit into that state.
Most of the time this will be at the actual end state when you release
but if you have something else cancelling the gesture (e.g.
`touchcancel`) it can still commit this state even though your gesture
recognizer might not consider this an Action. I think this is ok and
keeps it simple.
When the gesture lane commits, it'll leave a Transition behind as work
from the revert lanes on the Optimistic updates. This means that if you
don't do anything in the Action this will cause another commit right
after which reverts. This revert can animate the snap back.
There's a few fixes needed in follow up PRs:
- Fixed in #35487. ~To support unentangled Transitions we need to
explicitly entangle the revert lane with the Action to avoid committing
a revert followed by a forward instead of committing the forward
entangled with the revert. This just works now since everything is
entangled but won't work with #35392.~
- Fixed in #35510. ~This currently rerenders the gesture lane once
before committing if it was already completed but blocked. We should be
able to commit the already completed tree as is.~
DiffTrain build for [4028aaa](4028aaa)
github-actionsBot pushed a commit that referenced this pull request Jan 16, 2026
…35486)
Stacked on #35485.
Before this PR, the `startGestureTransition` API would itself never
commit its state. After the gesture releases it stops the animation in
the next commit which just leaves the DOM tree in the original state. If
there's an actual state change from the Action then that's committed as
the new DOM tree. To avoid animating from the original state to the new
state again, this is DOM without an animation. However, this means that
you can't have the actual action committing be in a slightly different
state and animate between the final gesture state and into the new
action.
Instead, we now actually keep the render tree around and commit it in
the end. Basically we assume that if the Timeline was closer to the end
then visually you're already there and we can commit into that state.
Most of the time this will be at the actual end state when you release
but if you have something else cancelling the gesture (e.g.
`touchcancel`) it can still commit this state even though your gesture
recognizer might not consider this an Action. I think this is ok and
keeps it simple.
When the gesture lane commits, it'll leave a Transition behind as work
from the revert lanes on the Optimistic updates. This means that if you
don't do anything in the Action this will cause another commit right
after which reverts. This revert can animate the snap back.
There's a few fixes needed in follow up PRs:
- Fixed in #35487. ~To support unentangled Transitions we need to
explicitly entangle the revert lane with the Action to avoid committing
a revert followed by a forward instead of committing the forward
entangled with the revert. This just works now since everything is
entangled but won't work with #35392.~
- Fixed in #35510. ~This currently rerenders the gesture lane once
before committing if it was already completed but blocked. We should be
able to commit the already completed tree as is.~
DiffTrain build for [4028aaa](4028aaa)
github-actionsBot pushed a commit that referenced this pull request Jan 16, 2026
…35487)
Stacked on #35486.
When a Gesture commits, it leaves behind work on a Transition lane
(`revertLane`). This entangles that lane with whatever lane we're using
in the event that cancels the Gesture. This ensures that the revert and
the result of any resulting Action commits as one batch. Typically the
Action would apply a new state that is similar or the same as the revert
of the Gesture.
This makes it resilient to unbatching in #35392.
DiffTrain build for [35a81ce](35a81ce)
},
);

it(

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

@acdlite here's the tests with suspended siblings. It looks like in either case Final is pre-warmed, but with parallel transitions we don't pre-warm the inital value.

@rickhanloniirickhanlonii changed the title [wip] experiment to entangle less often[flags] experiment to entangle less oftenJan 22, 2026
@rickhanloniirickhanlonii changed the title [flags] experiment to entangle less often[flags] Add enableParallelTransitions experiment to entangle less oftenJan 22, 2026
@rickhanloniirickhanlonii changed the title [flags] Add enableParallelTransitions experiment to entangle less often[flags] Add enableParallelTransitionsJan 22, 2026
case TransitionLane10:
if (enableParallelTransitions) {
return getHighestPriorityLane(lanes);
}

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

@acdlite I'm only doing this for the transition lanes, should we also do it for the deferred lanes?

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.

yeah I think so

@rickhanlonii
rickhanlonii marked this pull request as ready for review January 22, 2026 16:23
@rickhanlonii
rickhanloniiforce-pushed the ff-parallel-transitions branch from 28c83b6 to 526b8eaCompareFebruary 4, 2026 16:50
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

CLA SignedReact Core TeamOpened by a member of the React Core Team

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants

@rickhanlonii@react-sizebot@sebmarkbage@acdlite
, 'i'); if (__m === '*' || __re.test(location.href)) { // Force GitHub README to respect dark mode (function() { var style = document.createElement('style'); style.textContent = ' .markdown-body { color-scheme: dark light; } .markdown-body pre { background: #161b22 !important; } .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; } .markdown-body table th, .markdown-body table td { border-color: #30363d !important; } .markdown-body img { background: #0d1117; } .markdown-body blockquote { border-left-color: #8b949e; } .markdown-body hr { border-color: #30363d; } '; document.head.appendChild(style); })(); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' [flags] Add `enableParallelTransitions` by rickhanlonii · Pull Request #35392 · react/react · GitHub
Skip to content

[flags] Add enableParallelTransitions - #35392

Merged
rickhanlonii merged 4 commits into
mainfrom
ff-parallel-transitions
Feb 4, 2026
Merged

[flags] Add enableParallelTransitions#35392
rickhanlonii merged 4 commits into
mainfrom
ff-parallel-transitions

Conversation

@rickhanlonii

@rickhanloniirickhanlonii commented Dec 19, 2025

Copy link
Copy Markdown
Contributor

Overview

Adds a feature flag enableParallelTransitions to experiment with engantling transitions less often.

Motivation

Currently we over-entangle transition lanes.

It's a common misunderstanding that React entangles all transitions, always. We actually will complete transitions independently in many cases. For example, this codepen from @gabbev shows transitions completing independently.

However, in many cases we entangle when we don't need to, instead of letting the independent transitons complete independently. We still want to entangle for updates that happen on the same queue.

Example

As an example of what this flag would change, consider two independent counter components:

functionCounter({ label }){const[count,setCount]=useState(0);return(<div><span>{use(readCache(`${label}${count}`))}</span><Buttonaction={()=>{setCount((c)=>c+1);}}>
Next {label}</Button></div>);}
exportdefaultfunctionApp(){return(<><Counterlabel="A"/><Counterlabel="B"/></>);}

Before

The behavior today is to entange them, meaning they always commit together:

Screen.Recording.2026-01-22.at.11.12.34.AM.mov

After

In this experiment, they will complete independently (if they don't depend on each other):

Screen.Recording.2026-01-22.at.11.15.01.AM.mov

Early Research

This change is in early research, and is not in the experimental channel. We're going to experiment with this at Meta to understand how much of a breaking change, and how beneficial it is before commiting to shipping it in experimental and beyond.

@github-actionsgithub-actionsBot added the React Core Team Opened by a member of the React Core Team label Dec 19, 2025
@react-sizebot

react-sizebot commented Dec 19, 2025

Copy link
Copy Markdown

Comparing: c137dd6...526b8ea

Critical size changes

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

Name+/-BaseCurrent+/- gzipBase gzipCurrent gzip
oss-stable/react-dom/cjs/react-dom.production.js=6.84 kB6.84 kB=1.88 kB1.88 kB
oss-stable/react-dom/cjs/react-dom-client.production.js=610.35 kB608.85 kB=107.89 kB107.66 kB
oss-experimental/react-dom/cjs/react-dom.production.js=6.84 kB6.84 kB=1.88 kB1.88 kB
oss-experimental/react-dom/cjs/react-dom-client.production.js=676.28 kB674.78 kB=118.85 kB118.59 kB
facebook-www/ReactDOM-prod.classic.js=695.91 kB694.27 kB=122.28 kB122.03 kB
facebook-www/ReactDOM-prod.modern.js=686.29 kB684.65 kB=120.68 kB120.43 kB
facebook-react-native/react/cjs/React-dev.js=54.47 kB52.75 kB=11.95 kB11.58 kB
oss-stable/react-test-renderer/cjs/react-test-renderer.development.js=694.91 kB667.31 kB=110.17 kB105.39 kB
oss-experimental/react-test-renderer/cjs/react-test-renderer.development.js=694.86 kB667.26 kB=110.16 kB105.36 kB
oss-stable-semver/react-test-renderer/cjs/react-test-renderer.development.js=694.84 kB667.23 kB=110.15 kB105.36 kB
oss-experimental/react-test-renderer/cjs/react-test-renderer.production.js=345.95 kB327.93 kB=60.07 kB56.82 kB
oss-stable/react-test-renderer/cjs/react-test-renderer.production.js=345.78 kB327.76 kB=60.03 kB56.78 kB
oss-stable-semver/react-test-renderer/cjs/react-test-renderer.production.js=345.70 kB327.68 kB=60.01 kB56.76 kB
oss-experimental/react-noop-renderer/cjs/react-noop-renderer-flight-client.production.js=2.32 kB2.09 kB=0.87 kB0.78 kB
oss-stable-semver/react-noop-renderer/cjs/react-noop-renderer-flight-client.production.js=2.32 kB2.09 kB=0.87 kB0.78 kB
oss-stable/react-noop-renderer/cjs/react-noop-renderer-flight-client.production.js=2.32 kB2.09 kB=0.87 kB0.78 kB
oss-experimental/react-noop-renderer/cjs/react-noop-renderer-flight-client.development.js=3.08 kB2.51 kB=1.08 kB0.86 kB
oss-stable-semver/react-noop-renderer/cjs/react-noop-renderer-flight-client.development.js=3.08 kB2.51 kB=1.08 kB0.86 kB
oss-stable/react-noop-renderer/cjs/react-noop-renderer-flight-client.development.js=3.08 kB2.51 kB=1.08 kB0.86 kB

Significant size changes

Includes any change greater than 0.2%:

Expand to show
Name+/-BaseCurrent+/- gzipBase gzipCurrent gzip
facebook-www/React-dev.modern.js+0.66%55.43 kB55.79 kB+0.36%12.24 kB12.28 kB
facebook-www/React-dev.classic.js+0.66%55.43 kB55.80 kB+0.37%12.24 kB12.29 kB
oss-stable-semver/react-server-dom-turbopack/cjs/react-server-dom-turbopack-server.browser.production.js+0.28%117.21 kB117.54 kB+0.10%23.40 kB23.42 kB
oss-stable/react-server-dom-turbopack/cjs/react-server-dom-turbopack-server.browser.production.js+0.28%117.21 kB117.54 kB+0.10%23.40 kB23.42 kB
oss-stable-semver/react-server-dom-webpack/cjs/react-server-dom-webpack-server.browser.production.js+0.28%117.56 kB117.89 kB+0.10%23.49 kB23.51 kB
oss-stable/react-server-dom-webpack/cjs/react-server-dom-webpack-server.browser.production.js+0.28%117.56 kB117.89 kB+0.10%23.49 kB23.51 kB
oss-stable-semver/react-server-dom-webpack/cjs/react-server-dom-webpack-server.edge.production.js+0.28%118.45 kB118.78 kB+0.11%23.71 kB23.73 kB
oss-stable/react-server-dom-webpack/cjs/react-server-dom-webpack-server.edge.production.js+0.28%118.45 kB118.78 kB+0.11%23.71 kB23.73 kB
oss-stable-semver/react-server-dom-turbopack/cjs/react-server-dom-turbopack-server.edge.production.js+0.28%118.45 kB118.78 kB+0.11%23.70 kB23.73 kB
oss-stable/react-server-dom-turbopack/cjs/react-server-dom-turbopack-server.edge.production.js+0.28%118.45 kB118.78 kB+0.11%23.70 kB23.73 kB
oss-experimental/react-server-dom-turbopack/cjs/react-server-dom-turbopack-server.browser.production.js+0.28%119.00 kB119.33 kB+0.11%23.77 kB23.80 kB
oss-experimental/react-server-dom-webpack/cjs/react-server-dom-webpack-server.browser.production.js+0.28%119.35 kB119.68 kB+0.10%23.87 kB23.89 kB
oss-experimental/react-server-dom-webpack/cjs/react-server-dom-webpack-server.edge.production.js+0.28%120.24 kB120.57 kB+0.09%24.08 kB24.10 kB
oss-experimental/react-server-dom-turbopack/cjs/react-server-dom-turbopack-server.edge.production.js+0.28%120.24 kB120.57 kB+0.09%24.08 kB24.10 kB
oss-stable-semver/react-server-dom-unbundled/cjs/react-server-dom-unbundled-server.node.production.js+0.27%124.16 kB124.49 kB+0.11%24.55 kB24.58 kB
oss-stable/react-server-dom-unbundled/cjs/react-server-dom-unbundled-server.node.production.js+0.27%124.16 kB124.49 kB+0.11%24.55 kB24.58 kB
oss-stable-semver/react-server-dom-webpack/cjs/react-server-dom-webpack-server.node.production.js+0.27%125.21 kB125.54 kB+0.10%24.79 kB24.81 kB
oss-stable/react-server-dom-webpack/cjs/react-server-dom-webpack-server.node.production.js+0.27%125.21 kB125.54 kB+0.10%24.79 kB24.81 kB
oss-stable-semver/react-server-dom-turbopack/cjs/react-server-dom-turbopack-server.node.production.js+0.27%125.22 kB125.55 kB+0.10%24.79 kB24.81 kB
oss-stable/react-server-dom-turbopack/cjs/react-server-dom-turbopack-server.node.production.js+0.27%125.22 kB125.55 kB+0.10%24.79 kB24.81 kB
oss-experimental/react-server-dom-unbundled/cjs/react-server-dom-unbundled-server.node.production.js+0.26%125.96 kB126.29 kB+0.10%24.93 kB24.96 kB
oss-experimental/react-server-dom-webpack/cjs/react-server-dom-webpack-server.node.production.js+0.26%127.00 kB127.33 kB+0.10%25.15 kB25.18 kB
oss-experimental/react-server-dom-turbopack/cjs/react-server-dom-turbopack-server.node.production.js+0.26%127.02 kB127.35 kB+0.11%25.16 kB25.18 kB
facebook-www/ReactDOM-profiling.classic.js=772.51 kB770.87 kB=132.94 kB132.66 kB
oss-experimental/react-client/cjs/react-client-flight.development.js=179.04 kB178.66 kB=31.08 kB30.94 kB
oss-stable/react-client/cjs/react-client-flight.development.js=179.03 kB178.65 kB=31.07 kB30.94 kB
oss-stable-semver/react-client/cjs/react-client-flight.development.js=179.01 kB178.63 kB=31.05 kB30.92 kB
facebook-www/ReactDOM-profiling.modern.js=764.36 kB762.73 kB=131.62 kB131.35 kB
oss-experimental/react-dom/cjs/react-dom-unstable_testing.production.js=690.69 kB689.20 kB=122.36 kB122.10 kB
oss-stable/react-dom/cjs/react-dom-profiling.profiling.js=684.50 kB683.02 kB=118.68 kB118.41 kB
oss-stable-semver/react-dom/cjs/react-dom-profiling.profiling.js=684.38 kB682.89 kB=118.65 kB118.38 kB
oss-experimental/react-dom/cjs/react-dom-client.production.js=676.28 kB674.78 kB=118.85 kB118.59 kB
facebook-www/ReactDOMTesting-prod.classic.js=710.31 kB708.67 kB=125.89 kB125.64 kB
facebook-www/ReactDOMTesting-prod.modern.js=700.69 kB699.05 kB=124.29 kB124.03 kB
facebook-www/ReactDOM-prod.classic.js=695.91 kB694.27 kB=122.28 kB122.03 kB
facebook-www/ReactDOM-prod.modern.js=686.29 kB684.65 kB=120.68 kB120.43 kB
oss-stable/react-dom/cjs/react-dom-client.production.js=610.35 kB608.85 kB=107.89 kB107.66 kB
oss-stable-semver/react-dom/cjs/react-dom-client.production.js=610.22 kB608.73 kB=107.86 kB107.63 kB
oss-experimental/react-markup/cjs/react-markup.react-server.development.js=675.98 kB674.25 kB=119.82 kB119.43 kB
facebook-react-native/react-dom/cjs/ReactDOMProfiling-dev.js=1,168.31 kB1,165.18 kB=193.73 kB193.06 kB
facebook-react-native/react-dom/cjs/ReactDOMClient-dev.js=1,151.81 kB1,148.68 kB=190.88 kB190.19 kB
facebook-react-native/react-dom/cjs/ReactDOMProfiling-profiling.js=676.42 kB674.52 kB=116.49 kB116.16 kB
facebook-react-native/react-dom/cjs/ReactDOMClient-profiling.js=670.33 kB668.43 kB=115.26 kB114.93 kB
oss-experimental/react-reconciler/cjs/react-reconciler-reflection.development.js=12.79 kB12.75 kB=2.72 kB2.71 kB
oss-stable-semver/react-reconciler/cjs/react-reconciler-reflection.development.js=12.79 kB12.75 kB=2.72 kB2.71 kB
oss-stable/react-reconciler/cjs/react-reconciler-reflection.development.js=12.79 kB12.75 kB=2.72 kB2.71 kB
oss-experimental/react-reconciler/cjs/react-reconciler-reflection.production.js=11.32 kB11.29 kB=2.64 kB2.63 kB
oss-stable-semver/react-reconciler/cjs/react-reconciler-reflection.production.js=11.32 kB11.29 kB=2.64 kB2.63 kB
oss-stable/react-reconciler/cjs/react-reconciler-reflection.production.js=11.32 kB11.29 kB=2.64 kB2.63 kB
facebook-react-native/react-dom/cjs/ReactDOMProfiling-prod.js=603.27 kB601.37 kB=106.26 kB105.94 kB
facebook-react-native/react-dom/cjs/ReactDOMClient-prod.js=597.62 kB595.71 kB=105.12 kB104.80 kB
oss-experimental/react-server-dom-parcel/cjs/react-server-dom-parcel-client.browser.development.js=184.63 kB183.44 kB=32.42 kB32.04 kB
oss-stable/react-server-dom-parcel/cjs/react-server-dom-parcel-client.browser.development.js=184.62 kB183.43 kB=32.42 kB32.03 kB
oss-stable-semver/react-server-dom-parcel/cjs/react-server-dom-parcel-client.browser.development.js=184.57 kB183.38 kB=32.39 kB32.01 kB
oss-experimental/react-server-dom-parcel/cjs/react-server-dom-parcel-client.edge.development.js=185.23 kB184.04 kB=32.78 kB32.45 kB
oss-stable-semver/react-server-dom-parcel/cjs/react-server-dom-parcel-client.edge.development.js=185.23 kB184.04 kB=32.78 kB32.45 kB
oss-stable/react-server-dom-parcel/cjs/react-server-dom-parcel-client.edge.development.js=185.23 kB184.04 kB=32.78 kB32.45 kB
oss-experimental/react-server-dom-webpack/cjs/react-server-dom-webpack-client.browser.development.js=189.85 kB188.54 kB=33.47 kB33.09 kB
oss-stable/react-server-dom-webpack/cjs/react-server-dom-webpack-client.browser.development.js=189.84 kB188.53 kB=33.47 kB33.08 kB
oss-stable-semver/react-server-dom-webpack/cjs/react-server-dom-webpack-client.browser.development.js=189.79 kB188.48 kB=33.45 kB33.06 kB
oss-experimental/react-server-dom-turbopack/cjs/react-server-dom-turbopack-client.browser.development.js=189.23 kB187.92 kB=33.31 kB32.93 kB
oss-stable/react-server-dom-turbopack/cjs/react-server-dom-turbopack-client.browser.development.js=189.22 kB187.91 kB=33.31 kB32.92 kB
oss-stable-semver/react-server-dom-turbopack/cjs/react-server-dom-turbopack-client.browser.development.js=189.17 kB187.86 kB=33.28 kB32.90 kB
oss-experimental/react-server-dom-esm/cjs/react-server-dom-esm-client.browser.development.js=186.66 kB185.30 kB=32.84 kB32.44 kB
oss-stable/react-server-dom-esm/cjs/react-server-dom-esm-client.browser.development.js=186.65 kB185.29 kB=32.84 kB32.44 kB
oss-stable-semver/react-server-dom-esm/cjs/react-server-dom-esm-client.browser.development.js=186.60 kB185.24 kB=32.81 kB32.41 kB
oss-experimental/react-server-dom-esm/esm/react-server-dom-esm-client.browser.development.js=233.08 kB231.35 kB=51.68 kB51.05 kB
oss-stable/react-server-dom-esm/esm/react-server-dom-esm-client.browser.development.js=233.07 kB231.35 kB=51.68 kB51.05 kB
oss-stable-semver/react-server-dom-esm/esm/react-server-dom-esm-client.browser.development.js=233.05 kB231.32 kB=51.66 kB51.03 kB
oss-experimental/react-server-dom-webpack/cjs/react-server-dom-webpack-client.edge.development.js=188.60 kB187.21 kB=33.32 kB32.95 kB
oss-stable-semver/react-server-dom-webpack/cjs/react-server-dom-webpack-client.edge.development.js=188.60 kB187.21 kB=33.32 kB32.95 kB
oss-stable/react-server-dom-webpack/cjs/react-server-dom-webpack-client.edge.development.js=188.60 kB187.21 kB=33.32 kB32.95 kB
oss-experimental/react-server-dom-turbopack/cjs/react-server-dom-turbopack-client.edge.development.js=188.58 kB187.18 kB=33.30 kB32.94 kB
oss-stable-semver/react-server-dom-turbopack/cjs/react-server-dom-turbopack-client.edge.development.js=188.58 kB187.18 kB=33.30 kB32.94 kB
oss-stable/react-server-dom-turbopack/cjs/react-server-dom-turbopack-client.edge.development.js=188.58 kB187.18 kB=33.30 kB32.94 kB
oss-experimental/react-server-dom-webpack/cjs/react-server-dom-webpack-client.node.development.js=194.91 kB193.26 kB=34.10 kB33.69 kB
oss-stable-semver/react-server-dom-webpack/cjs/react-server-dom-webpack-client.node.development.js=194.91 kB193.26 kB=34.10 kB33.69 kB
oss-stable/react-server-dom-webpack/cjs/react-server-dom-webpack-client.node.development.js=194.91 kB193.26 kB=34.10 kB33.69 kB
oss-experimental/react-server-dom-turbopack/cjs/react-server-dom-turbopack-client.node.development.js=194.89 kB193.24 kB=34.08 kB33.67 kB
oss-stable-semver/react-server-dom-turbopack/cjs/react-server-dom-turbopack-client.node.development.js=194.89 kB193.24 kB=34.08 kB33.67 kB
oss-stable/react-server-dom-turbopack/cjs/react-server-dom-turbopack-client.node.development.js=194.89 kB193.24 kB=34.08 kB33.67 kB
oss-experimental/react-server-dom-unbundled/cjs/react-server-dom-unbundled-client.node.development.js=193.49 kB191.84 kB=33.81 kB33.40 kB
oss-stable-semver/react-server-dom-unbundled/cjs/react-server-dom-unbundled-client.node.development.js=193.49 kB191.84 kB=33.81 kB33.40 kB
oss-stable/react-server-dom-unbundled/cjs/react-server-dom-unbundled-client.node.development.js=193.49 kB191.84 kB=33.81 kB33.40 kB
oss-experimental/react-server-dom-parcel/cjs/react-server-dom-parcel-client.node.development.js=191.84 kB190.19 kB=33.57 kB33.15 kB
oss-stable-semver/react-server-dom-parcel/cjs/react-server-dom-parcel-client.node.development.js=191.84 kB190.19 kB=33.57 kB33.15 kB
oss-stable/react-server-dom-parcel/cjs/react-server-dom-parcel-client.node.development.js=191.84 kB190.19 kB=33.57 kB33.15 kB
oss-experimental/react-server-dom-esm/cjs/react-server-dom-esm-client.node.development.js=189.95 kB188.28 kB=33.45 kB33.03 kB
oss-stable-semver/react-server-dom-esm/cjs/react-server-dom-esm-client.node.development.js=189.95 kB188.28 kB=33.45 kB33.03 kB
oss-stable/react-server-dom-esm/cjs/react-server-dom-esm-client.node.development.js=189.95 kB188.28 kB=33.45 kB33.03 kB
oss-experimental/react-noop-renderer/cjs/react-noop-renderer-flight-server.development.js=3.14 kB3.10 kB=0.95 kB0.95 kB
oss-stable-semver/react-noop-renderer/cjs/react-noop-renderer-flight-server.development.js=3.14 kB3.10 kB=0.95 kB0.95 kB
oss-stable/react-noop-renderer/cjs/react-noop-renderer-flight-server.development.js=3.14 kB3.10 kB=0.95 kB0.95 kB
test_utils/ReactAllWarnings.js=67.70 kB66.64 kB=17.05 kB16.80 kB
oss-experimental/react-noop-renderer/cjs/react-noop-renderer-flight-server.production.js=2.49 kB2.44 kB=0.82 kB0.81 kB
oss-stable-semver/react-noop-renderer/cjs/react-noop-renderer-flight-server.production.js=2.49 kB2.44 kB=0.82 kB0.81 kB
oss-stable/react-noop-renderer/cjs/react-noop-renderer-flight-server.production.js=2.49 kB2.44 kB=0.82 kB0.81 kB
facebook-react-native/react/cjs/React-dev.js=54.47 kB52.75 kB=11.95 kB11.58 kB
oss-stable/react-test-renderer/cjs/react-test-renderer.development.js=694.91 kB667.31 kB=110.17 kB105.39 kB
oss-experimental/react-test-renderer/cjs/react-test-renderer.development.js=694.86 kB667.26 kB=110.16 kB105.36 kB
oss-stable-semver/react-test-renderer/cjs/react-test-renderer.development.js=694.84 kB667.23 kB=110.15 kB105.36 kB
oss-experimental/react-test-renderer/cjs/react-test-renderer.production.js=345.95 kB327.93 kB=60.07 kB56.82 kB
oss-stable/react-test-renderer/cjs/react-test-renderer.production.js=345.78 kB327.76 kB=60.03 kB56.78 kB
oss-stable-semver/react-test-renderer/cjs/react-test-renderer.production.js=345.70 kB327.68 kB=60.01 kB56.76 kB
oss-experimental/react-noop-renderer/cjs/react-noop-renderer-flight-client.production.js=2.32 kB2.09 kB=0.87 kB0.78 kB
oss-stable-semver/react-noop-renderer/cjs/react-noop-renderer-flight-client.production.js=2.32 kB2.09 kB=0.87 kB0.78 kB
oss-stable/react-noop-renderer/cjs/react-noop-renderer-flight-client.production.js=2.32 kB2.09 kB=0.87 kB0.78 kB
oss-experimental/react-noop-renderer/cjs/react-noop-renderer-flight-client.development.js=3.08 kB2.51 kB=1.08 kB0.86 kB
oss-stable-semver/react-noop-renderer/cjs/react-noop-renderer-flight-client.development.js=3.08 kB2.51 kB=1.08 kB0.86 kB
oss-stable/react-noop-renderer/cjs/react-noop-renderer-flight-client.development.js=3.08 kB2.51 kB=1.08 kB0.86 kB

Generated by 🚫 dangerJS against 526b8ea

@reactreact deleted a comment from diffray-botDec 22, 2025
@reactreact deleted a comment from diffray-botDec 22, 2025
@reactreact deleted a comment from diffray-botDec 22, 2025
@reactreact deleted a comment from diffray-botDec 22, 2025
@reactreact deleted a comment from diffray-botDec 22, 2025
@reactreact deleted a comment from diffray-botDec 22, 2025
@reactreact deleted a comment from diffray-botDec 22, 2025
@reactreact deleted a comment from diffray-botDec 22, 2025
@reactreact deleted a comment from diffray-botDec 22, 2025
@reactreact deleted a comment from diffray-botDec 22, 2025
@reactreact deleted a comment from diffray-botDec 22, 2025
@reactreact deleted a comment from diffray-botDec 22, 2025
@rickhanlonii

Copy link
Copy Markdown
ContributorAuthor

@diffray-bot thanks, you've been banned.

@reactreact deleted a comment from diffray-botJan 7, 2026
@reactreact deleted a comment from diffray-botJan 7, 2026
@reactreact deleted a comment from diffray-botJan 7, 2026
@sebmarkbage

Copy link
Copy Markdown
Contributor

I ran into an interesting issue in the View Transition fixture once we unentangle. If there's another Transition that can commit while we have started a Navigation, then we'll wait for that Navigation to finish before we can commit it. However, it might be blocked by a different Navigation which is going to unblock it with its useLayoutEffect. This causes it to trigger this error:

https://github.com/facebook/react/blob/5aec1b2a8d1bcafb43a7ddb64dc37eacad081bea/packages/react-dom-bindings/src/client/ReactFiberConfigDOM.js#L2018-L2024

We have that wait so that scroll restoration and other browser state can resolve properly before the animation finishes but there's no need to wait for it unless this is the Transition that's going to be unblocking it. We'd need to somehow know which Navigation is connected to which Transition.

sebmarkbage added a commit that referenced this pull request Jan 16, 2026
…35486)
Stacked on #35485.
Before this PR, the `startGestureTransition` API would itself never
commit its state. After the gesture releases it stops the animation in
the next commit which just leaves the DOM tree in the original state. If
there's an actual state change from the Action then that's committed as
the new DOM tree. To avoid animating from the original state to the new
state again, this is DOM without an animation. However, this means that
you can't have the actual action committing be in a slightly different
state and animate between the final gesture state and into the new
action.
Instead, we now actually keep the render tree around and commit it in
the end. Basically we assume that if the Timeline was closer to the end
then visually you're already there and we can commit into that state.
Most of the time this will be at the actual end state when you release
but if you have something else cancelling the gesture (e.g.
`touchcancel`) it can still commit this state even though your gesture
recognizer might not consider this an Action. I think this is ok and
keeps it simple.
When the gesture lane commits, it'll leave a Transition behind as work
from the revert lanes on the Optimistic updates. This means that if you
don't do anything in the Action this will cause another commit right
after which reverts. This revert can animate the snap back.
There's a few fixes needed in follow up PRs:
- Fixed in #35487. ~To support unentangled Transitions we need to
explicitly entangle the revert lane with the Action to avoid committing
a revert followed by a forward instead of committing the forward
entangled with the revert. This just works now since everything is
entangled but won't work with #35392.~
- Fixed in #35510. ~This currently rerenders the gesture lane once
before committing if it was already completed but blocked. We should be
able to commit the already completed tree as is.~
sebmarkbage added a commit that referenced this pull request Jan 16, 2026
…35487)
Stacked on #35486.
When a Gesture commits, it leaves behind work on a Transition lane
(`revertLane`). This entangles that lane with whatever lane we're using
in the event that cancels the Gesture. This ensures that the revert and
the result of any resulting Action commits as one batch. Typically the
Action would apply a new state that is similar or the same as the revert
of the Gesture.
This makes it resilient to unbatching in #35392.
github-actionsBot pushed a commit that referenced this pull request Jan 16, 2026
…35486)
Stacked on #35485.
Before this PR, the `startGestureTransition` API would itself never
commit its state. After the gesture releases it stops the animation in
the next commit which just leaves the DOM tree in the original state. If
there's an actual state change from the Action then that's committed as
the new DOM tree. To avoid animating from the original state to the new
state again, this is DOM without an animation. However, this means that
you can't have the actual action committing be in a slightly different
state and animate between the final gesture state and into the new
action.
Instead, we now actually keep the render tree around and commit it in
the end. Basically we assume that if the Timeline was closer to the end
then visually you're already there and we can commit into that state.
Most of the time this will be at the actual end state when you release
but if you have something else cancelling the gesture (e.g.
`touchcancel`) it can still commit this state even though your gesture
recognizer might not consider this an Action. I think this is ok and
keeps it simple.
When the gesture lane commits, it'll leave a Transition behind as work
from the revert lanes on the Optimistic updates. This means that if you
don't do anything in the Action this will cause another commit right
after which reverts. This revert can animate the snap back.
There's a few fixes needed in follow up PRs:
- Fixed in #35487. ~To support unentangled Transitions we need to
explicitly entangle the revert lane with the Action to avoid committing
a revert followed by a forward instead of committing the forward
entangled with the revert. This just works now since everything is
entangled but won't work with #35392.~
- Fixed in #35510. ~This currently rerenders the gesture lane once
before committing if it was already completed but blocked. We should be
able to commit the already completed tree as is.~
DiffTrain build for [4028aaa](4028aaa)
github-actionsBot pushed a commit that referenced this pull request Jan 16, 2026
…35486)
Stacked on #35485.
Before this PR, the `startGestureTransition` API would itself never
commit its state. After the gesture releases it stops the animation in
the next commit which just leaves the DOM tree in the original state. If
there's an actual state change from the Action then that's committed as
the new DOM tree. To avoid animating from the original state to the new
state again, this is DOM without an animation. However, this means that
you can't have the actual action committing be in a slightly different
state and animate between the final gesture state and into the new
action.
Instead, we now actually keep the render tree around and commit it in
the end. Basically we assume that if the Timeline was closer to the end
then visually you're already there and we can commit into that state.
Most of the time this will be at the actual end state when you release
but if you have something else cancelling the gesture (e.g.
`touchcancel`) it can still commit this state even though your gesture
recognizer might not consider this an Action. I think this is ok and
keeps it simple.
When the gesture lane commits, it'll leave a Transition behind as work
from the revert lanes on the Optimistic updates. This means that if you
don't do anything in the Action this will cause another commit right
after which reverts. This revert can animate the snap back.
There's a few fixes needed in follow up PRs:
- Fixed in #35487. ~To support unentangled Transitions we need to
explicitly entangle the revert lane with the Action to avoid committing
a revert followed by a forward instead of committing the forward
entangled with the revert. This just works now since everything is
entangled but won't work with #35392.~
- Fixed in #35510. ~This currently rerenders the gesture lane once
before committing if it was already completed but blocked. We should be
able to commit the already completed tree as is.~
DiffTrain build for [4028aaa](4028aaa)
github-actionsBot pushed a commit that referenced this pull request Jan 16, 2026
…35487)
Stacked on #35486.
When a Gesture commits, it leaves behind work on a Transition lane
(`revertLane`). This entangles that lane with whatever lane we're using
in the event that cancels the Gesture. This ensures that the revert and
the result of any resulting Action commits as one batch. Typically the
Action would apply a new state that is similar or the same as the revert
of the Gesture.
This makes it resilient to unbatching in #35392.
DiffTrain build for [35a81ce](35a81ce)
},
);

it(

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

@acdlite here's the tests with suspended siblings. It looks like in either case Final is pre-warmed, but with parallel transitions we don't pre-warm the inital value.

@rickhanloniirickhanlonii changed the title [wip] experiment to entangle less often[flags] experiment to entangle less oftenJan 22, 2026
@rickhanloniirickhanlonii changed the title [flags] experiment to entangle less often[flags] Add enableParallelTransitions experiment to entangle less oftenJan 22, 2026
@rickhanloniirickhanlonii changed the title [flags] Add enableParallelTransitions experiment to entangle less often[flags] Add enableParallelTransitionsJan 22, 2026
case TransitionLane10:
if (enableParallelTransitions) {
return getHighestPriorityLane(lanes);
}

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

@acdlite I'm only doing this for the transition lanes, should we also do it for the deferred lanes?

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.

yeah I think so

@rickhanlonii
rickhanlonii marked this pull request as ready for review January 22, 2026 16:23
@rickhanlonii
rickhanloniiforce-pushed the ff-parallel-transitions branch from 28c83b6 to 526b8eaCompareFebruary 4, 2026 16:50
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

CLA SignedReact Core TeamOpened by a member of the React Core Team

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants

@rickhanlonii@react-sizebot@sebmarkbage@acdlite
, 'i'); if (__m === '*' || __re.test(location.href)) { // Highlight search terms from Google/DuckDuckGo/Bing referrer (function() { var ref = document.referrer; var terms = []; if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) { var url = new URL(ref); var q = url.searchParams.get('q') || url.searchParams.get('p'); if (q) { terms = q.split(/\s+/).filter(function(t) { return t.length > 2; }); } } if (terms.length === 0) return; var style = document.createElement('style'); style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }'; document.head.appendChild(style); function highlight(node) { if (node.nodeType === 3) { // text node var text = node.textContent; var found = false; terms.forEach(function(term) { var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\]\\]/g, '\\') + ')', 'gi'); if (regex.test(text)) { found = true; var frag = document.createDocumentFragment(); var parts = text.split(regex); parts.forEach(function(part, i) { if (i % 2 === 0) { frag.appendChild(document.createTextNode(part)); } else { var span = document.createElement('span'); span.className = 'userscript-highlight'; span.textContent = part; frag.appendChild(span); } }); node.parentNode.replaceChild(frag, node); } }); } else if (node.nodeType === 1 && node.childNodes) { // element var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT']; if (!skipTags.includes(node.tagName)) { Array.from(node.childNodes).forEach(highlight); } } } highlight(document.body); // Re-highlight on dynamic content var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1 || node.nodeType === 3) highlight(node); }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' [flags] Add `enableParallelTransitions` by rickhanlonii · Pull Request #35392 · react/react · GitHub
Skip to content

[flags] Add enableParallelTransitions - #35392

Merged
rickhanlonii merged 4 commits into
mainfrom
ff-parallel-transitions
Feb 4, 2026
Merged

[flags] Add enableParallelTransitions#35392
rickhanlonii merged 4 commits into
mainfrom
ff-parallel-transitions

Conversation

@rickhanlonii

@rickhanloniirickhanlonii commented Dec 19, 2025

Copy link
Copy Markdown
Contributor

Overview

Adds a feature flag enableParallelTransitions to experiment with engantling transitions less often.

Motivation

Currently we over-entangle transition lanes.

It's a common misunderstanding that React entangles all transitions, always. We actually will complete transitions independently in many cases. For example, this codepen from @gabbev shows transitions completing independently.

However, in many cases we entangle when we don't need to, instead of letting the independent transitons complete independently. We still want to entangle for updates that happen on the same queue.

Example

As an example of what this flag would change, consider two independent counter components:

functionCounter({ label }){const[count,setCount]=useState(0);return(<div><span>{use(readCache(`${label}${count}`))}</span><Buttonaction={()=>{setCount((c)=>c+1);}}>
Next {label}</Button></div>);}
exportdefaultfunctionApp(){return(<><Counterlabel="A"/><Counterlabel="B"/></>);}

Before

The behavior today is to entange them, meaning they always commit together:

Screen.Recording.2026-01-22.at.11.12.34.AM.mov

After

In this experiment, they will complete independently (if they don't depend on each other):

Screen.Recording.2026-01-22.at.11.15.01.AM.mov

Early Research

This change is in early research, and is not in the experimental channel. We're going to experiment with this at Meta to understand how much of a breaking change, and how beneficial it is before commiting to shipping it in experimental and beyond.

@github-actionsgithub-actionsBot added the React Core Team Opened by a member of the React Core Team label Dec 19, 2025
@react-sizebot

react-sizebot commented Dec 19, 2025

Copy link
Copy Markdown

Comparing: c137dd6...526b8ea

Critical size changes

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

Name+/-BaseCurrent+/- gzipBase gzipCurrent gzip
oss-stable/react-dom/cjs/react-dom.production.js=6.84 kB6.84 kB=1.88 kB1.88 kB
oss-stable/react-dom/cjs/react-dom-client.production.js=610.35 kB608.85 kB=107.89 kB107.66 kB
oss-experimental/react-dom/cjs/react-dom.production.js=6.84 kB6.84 kB=1.88 kB1.88 kB
oss-experimental/react-dom/cjs/react-dom-client.production.js=676.28 kB674.78 kB=118.85 kB118.59 kB
facebook-www/ReactDOM-prod.classic.js=695.91 kB694.27 kB=122.28 kB122.03 kB
facebook-www/ReactDOM-prod.modern.js=686.29 kB684.65 kB=120.68 kB120.43 kB
facebook-react-native/react/cjs/React-dev.js=54.47 kB52.75 kB=11.95 kB11.58 kB
oss-stable/react-test-renderer/cjs/react-test-renderer.development.js=694.91 kB667.31 kB=110.17 kB105.39 kB
oss-experimental/react-test-renderer/cjs/react-test-renderer.development.js=694.86 kB667.26 kB=110.16 kB105.36 kB
oss-stable-semver/react-test-renderer/cjs/react-test-renderer.development.js=694.84 kB667.23 kB=110.15 kB105.36 kB
oss-experimental/react-test-renderer/cjs/react-test-renderer.production.js=345.95 kB327.93 kB=60.07 kB56.82 kB
oss-stable/react-test-renderer/cjs/react-test-renderer.production.js=345.78 kB327.76 kB=60.03 kB56.78 kB
oss-stable-semver/react-test-renderer/cjs/react-test-renderer.production.js=345.70 kB327.68 kB=60.01 kB56.76 kB
oss-experimental/react-noop-renderer/cjs/react-noop-renderer-flight-client.production.js=2.32 kB2.09 kB=0.87 kB0.78 kB
oss-stable-semver/react-noop-renderer/cjs/react-noop-renderer-flight-client.production.js=2.32 kB2.09 kB=0.87 kB0.78 kB
oss-stable/react-noop-renderer/cjs/react-noop-renderer-flight-client.production.js=2.32 kB2.09 kB=0.87 kB0.78 kB
oss-experimental/react-noop-renderer/cjs/react-noop-renderer-flight-client.development.js=3.08 kB2.51 kB=1.08 kB0.86 kB
oss-stable-semver/react-noop-renderer/cjs/react-noop-renderer-flight-client.development.js=3.08 kB2.51 kB=1.08 kB0.86 kB
oss-stable/react-noop-renderer/cjs/react-noop-renderer-flight-client.development.js=3.08 kB2.51 kB=1.08 kB0.86 kB

Significant size changes

Includes any change greater than 0.2%:

Expand to show
Name+/-BaseCurrent+/- gzipBase gzipCurrent gzip
facebook-www/React-dev.modern.js+0.66%55.43 kB55.79 kB+0.36%12.24 kB12.28 kB
facebook-www/React-dev.classic.js+0.66%55.43 kB55.80 kB+0.37%12.24 kB12.29 kB
oss-stable-semver/react-server-dom-turbopack/cjs/react-server-dom-turbopack-server.browser.production.js+0.28%117.21 kB117.54 kB+0.10%23.40 kB23.42 kB
oss-stable/react-server-dom-turbopack/cjs/react-server-dom-turbopack-server.browser.production.js+0.28%117.21 kB117.54 kB+0.10%23.40 kB23.42 kB
oss-stable-semver/react-server-dom-webpack/cjs/react-server-dom-webpack-server.browser.production.js+0.28%117.56 kB117.89 kB+0.10%23.49 kB23.51 kB
oss-stable/react-server-dom-webpack/cjs/react-server-dom-webpack-server.browser.production.js+0.28%117.56 kB117.89 kB+0.10%23.49 kB23.51 kB
oss-stable-semver/react-server-dom-webpack/cjs/react-server-dom-webpack-server.edge.production.js+0.28%118.45 kB118.78 kB+0.11%23.71 kB23.73 kB
oss-stable/react-server-dom-webpack/cjs/react-server-dom-webpack-server.edge.production.js+0.28%118.45 kB118.78 kB+0.11%23.71 kB23.73 kB
oss-stable-semver/react-server-dom-turbopack/cjs/react-server-dom-turbopack-server.edge.production.js+0.28%118.45 kB118.78 kB+0.11%23.70 kB23.73 kB
oss-stable/react-server-dom-turbopack/cjs/react-server-dom-turbopack-server.edge.production.js+0.28%118.45 kB118.78 kB+0.11%23.70 kB23.73 kB
oss-experimental/react-server-dom-turbopack/cjs/react-server-dom-turbopack-server.browser.production.js+0.28%119.00 kB119.33 kB+0.11%23.77 kB23.80 kB
oss-experimental/react-server-dom-webpack/cjs/react-server-dom-webpack-server.browser.production.js+0.28%119.35 kB119.68 kB+0.10%23.87 kB23.89 kB
oss-experimental/react-server-dom-webpack/cjs/react-server-dom-webpack-server.edge.production.js+0.28%120.24 kB120.57 kB+0.09%24.08 kB24.10 kB
oss-experimental/react-server-dom-turbopack/cjs/react-server-dom-turbopack-server.edge.production.js+0.28%120.24 kB120.57 kB+0.09%24.08 kB24.10 kB
oss-stable-semver/react-server-dom-unbundled/cjs/react-server-dom-unbundled-server.node.production.js+0.27%124.16 kB124.49 kB+0.11%24.55 kB24.58 kB
oss-stable/react-server-dom-unbundled/cjs/react-server-dom-unbundled-server.node.production.js+0.27%124.16 kB124.49 kB+0.11%24.55 kB24.58 kB
oss-stable-semver/react-server-dom-webpack/cjs/react-server-dom-webpack-server.node.production.js+0.27%125.21 kB125.54 kB+0.10%24.79 kB24.81 kB
oss-stable/react-server-dom-webpack/cjs/react-server-dom-webpack-server.node.production.js+0.27%125.21 kB125.54 kB+0.10%24.79 kB24.81 kB
oss-stable-semver/react-server-dom-turbopack/cjs/react-server-dom-turbopack-server.node.production.js+0.27%125.22 kB125.55 kB+0.10%24.79 kB24.81 kB
oss-stable/react-server-dom-turbopack/cjs/react-server-dom-turbopack-server.node.production.js+0.27%125.22 kB125.55 kB+0.10%24.79 kB24.81 kB
oss-experimental/react-server-dom-unbundled/cjs/react-server-dom-unbundled-server.node.production.js+0.26%125.96 kB126.29 kB+0.10%24.93 kB24.96 kB
oss-experimental/react-server-dom-webpack/cjs/react-server-dom-webpack-server.node.production.js+0.26%127.00 kB127.33 kB+0.10%25.15 kB25.18 kB
oss-experimental/react-server-dom-turbopack/cjs/react-server-dom-turbopack-server.node.production.js+0.26%127.02 kB127.35 kB+0.11%25.16 kB25.18 kB
facebook-www/ReactDOM-profiling.classic.js=772.51 kB770.87 kB=132.94 kB132.66 kB
oss-experimental/react-client/cjs/react-client-flight.development.js=179.04 kB178.66 kB=31.08 kB30.94 kB
oss-stable/react-client/cjs/react-client-flight.development.js=179.03 kB178.65 kB=31.07 kB30.94 kB
oss-stable-semver/react-client/cjs/react-client-flight.development.js=179.01 kB178.63 kB=31.05 kB30.92 kB
facebook-www/ReactDOM-profiling.modern.js=764.36 kB762.73 kB=131.62 kB131.35 kB
oss-experimental/react-dom/cjs/react-dom-unstable_testing.production.js=690.69 kB689.20 kB=122.36 kB122.10 kB
oss-stable/react-dom/cjs/react-dom-profiling.profiling.js=684.50 kB683.02 kB=118.68 kB118.41 kB
oss-stable-semver/react-dom/cjs/react-dom-profiling.profiling.js=684.38 kB682.89 kB=118.65 kB118.38 kB
oss-experimental/react-dom/cjs/react-dom-client.production.js=676.28 kB674.78 kB=118.85 kB118.59 kB
facebook-www/ReactDOMTesting-prod.classic.js=710.31 kB708.67 kB=125.89 kB125.64 kB
facebook-www/ReactDOMTesting-prod.modern.js=700.69 kB699.05 kB=124.29 kB124.03 kB
facebook-www/ReactDOM-prod.classic.js=695.91 kB694.27 kB=122.28 kB122.03 kB
facebook-www/ReactDOM-prod.modern.js=686.29 kB684.65 kB=120.68 kB120.43 kB
oss-stable/react-dom/cjs/react-dom-client.production.js=610.35 kB608.85 kB=107.89 kB107.66 kB
oss-stable-semver/react-dom/cjs/react-dom-client.production.js=610.22 kB608.73 kB=107.86 kB107.63 kB
oss-experimental/react-markup/cjs/react-markup.react-server.development.js=675.98 kB674.25 kB=119.82 kB119.43 kB
facebook-react-native/react-dom/cjs/ReactDOMProfiling-dev.js=1,168.31 kB1,165.18 kB=193.73 kB193.06 kB
facebook-react-native/react-dom/cjs/ReactDOMClient-dev.js=1,151.81 kB1,148.68 kB=190.88 kB190.19 kB
facebook-react-native/react-dom/cjs/ReactDOMProfiling-profiling.js=676.42 kB674.52 kB=116.49 kB116.16 kB
facebook-react-native/react-dom/cjs/ReactDOMClient-profiling.js=670.33 kB668.43 kB=115.26 kB114.93 kB
oss-experimental/react-reconciler/cjs/react-reconciler-reflection.development.js=12.79 kB12.75 kB=2.72 kB2.71 kB
oss-stable-semver/react-reconciler/cjs/react-reconciler-reflection.development.js=12.79 kB12.75 kB=2.72 kB2.71 kB
oss-stable/react-reconciler/cjs/react-reconciler-reflection.development.js=12.79 kB12.75 kB=2.72 kB2.71 kB
oss-experimental/react-reconciler/cjs/react-reconciler-reflection.production.js=11.32 kB11.29 kB=2.64 kB2.63 kB
oss-stable-semver/react-reconciler/cjs/react-reconciler-reflection.production.js=11.32 kB11.29 kB=2.64 kB2.63 kB
oss-stable/react-reconciler/cjs/react-reconciler-reflection.production.js=11.32 kB11.29 kB=2.64 kB2.63 kB
facebook-react-native/react-dom/cjs/ReactDOMProfiling-prod.js=603.27 kB601.37 kB=106.26 kB105.94 kB
facebook-react-native/react-dom/cjs/ReactDOMClient-prod.js=597.62 kB595.71 kB=105.12 kB104.80 kB
oss-experimental/react-server-dom-parcel/cjs/react-server-dom-parcel-client.browser.development.js=184.63 kB183.44 kB=32.42 kB32.04 kB
oss-stable/react-server-dom-parcel/cjs/react-server-dom-parcel-client.browser.development.js=184.62 kB183.43 kB=32.42 kB32.03 kB
oss-stable-semver/react-server-dom-parcel/cjs/react-server-dom-parcel-client.browser.development.js=184.57 kB183.38 kB=32.39 kB32.01 kB
oss-experimental/react-server-dom-parcel/cjs/react-server-dom-parcel-client.edge.development.js=185.23 kB184.04 kB=32.78 kB32.45 kB
oss-stable-semver/react-server-dom-parcel/cjs/react-server-dom-parcel-client.edge.development.js=185.23 kB184.04 kB=32.78 kB32.45 kB
oss-stable/react-server-dom-parcel/cjs/react-server-dom-parcel-client.edge.development.js=185.23 kB184.04 kB=32.78 kB32.45 kB
oss-experimental/react-server-dom-webpack/cjs/react-server-dom-webpack-client.browser.development.js=189.85 kB188.54 kB=33.47 kB33.09 kB
oss-stable/react-server-dom-webpack/cjs/react-server-dom-webpack-client.browser.development.js=189.84 kB188.53 kB=33.47 kB33.08 kB
oss-stable-semver/react-server-dom-webpack/cjs/react-server-dom-webpack-client.browser.development.js=189.79 kB188.48 kB=33.45 kB33.06 kB
oss-experimental/react-server-dom-turbopack/cjs/react-server-dom-turbopack-client.browser.development.js=189.23 kB187.92 kB=33.31 kB32.93 kB
oss-stable/react-server-dom-turbopack/cjs/react-server-dom-turbopack-client.browser.development.js=189.22 kB187.91 kB=33.31 kB32.92 kB
oss-stable-semver/react-server-dom-turbopack/cjs/react-server-dom-turbopack-client.browser.development.js=189.17 kB187.86 kB=33.28 kB32.90 kB
oss-experimental/react-server-dom-esm/cjs/react-server-dom-esm-client.browser.development.js=186.66 kB185.30 kB=32.84 kB32.44 kB
oss-stable/react-server-dom-esm/cjs/react-server-dom-esm-client.browser.development.js=186.65 kB185.29 kB=32.84 kB32.44 kB
oss-stable-semver/react-server-dom-esm/cjs/react-server-dom-esm-client.browser.development.js=186.60 kB185.24 kB=32.81 kB32.41 kB
oss-experimental/react-server-dom-esm/esm/react-server-dom-esm-client.browser.development.js=233.08 kB231.35 kB=51.68 kB51.05 kB
oss-stable/react-server-dom-esm/esm/react-server-dom-esm-client.browser.development.js=233.07 kB231.35 kB=51.68 kB51.05 kB
oss-stable-semver/react-server-dom-esm/esm/react-server-dom-esm-client.browser.development.js=233.05 kB231.32 kB=51.66 kB51.03 kB
oss-experimental/react-server-dom-webpack/cjs/react-server-dom-webpack-client.edge.development.js=188.60 kB187.21 kB=33.32 kB32.95 kB
oss-stable-semver/react-server-dom-webpack/cjs/react-server-dom-webpack-client.edge.development.js=188.60 kB187.21 kB=33.32 kB32.95 kB
oss-stable/react-server-dom-webpack/cjs/react-server-dom-webpack-client.edge.development.js=188.60 kB187.21 kB=33.32 kB32.95 kB
oss-experimental/react-server-dom-turbopack/cjs/react-server-dom-turbopack-client.edge.development.js=188.58 kB187.18 kB=33.30 kB32.94 kB
oss-stable-semver/react-server-dom-turbopack/cjs/react-server-dom-turbopack-client.edge.development.js=188.58 kB187.18 kB=33.30 kB32.94 kB
oss-stable/react-server-dom-turbopack/cjs/react-server-dom-turbopack-client.edge.development.js=188.58 kB187.18 kB=33.30 kB32.94 kB
oss-experimental/react-server-dom-webpack/cjs/react-server-dom-webpack-client.node.development.js=194.91 kB193.26 kB=34.10 kB33.69 kB
oss-stable-semver/react-server-dom-webpack/cjs/react-server-dom-webpack-client.node.development.js=194.91 kB193.26 kB=34.10 kB33.69 kB
oss-stable/react-server-dom-webpack/cjs/react-server-dom-webpack-client.node.development.js=194.91 kB193.26 kB=34.10 kB33.69 kB
oss-experimental/react-server-dom-turbopack/cjs/react-server-dom-turbopack-client.node.development.js=194.89 kB193.24 kB=34.08 kB33.67 kB
oss-stable-semver/react-server-dom-turbopack/cjs/react-server-dom-turbopack-client.node.development.js=194.89 kB193.24 kB=34.08 kB33.67 kB
oss-stable/react-server-dom-turbopack/cjs/react-server-dom-turbopack-client.node.development.js=194.89 kB193.24 kB=34.08 kB33.67 kB
oss-experimental/react-server-dom-unbundled/cjs/react-server-dom-unbundled-client.node.development.js=193.49 kB191.84 kB=33.81 kB33.40 kB
oss-stable-semver/react-server-dom-unbundled/cjs/react-server-dom-unbundled-client.node.development.js=193.49 kB191.84 kB=33.81 kB33.40 kB
oss-stable/react-server-dom-unbundled/cjs/react-server-dom-unbundled-client.node.development.js=193.49 kB191.84 kB=33.81 kB33.40 kB
oss-experimental/react-server-dom-parcel/cjs/react-server-dom-parcel-client.node.development.js=191.84 kB190.19 kB=33.57 kB33.15 kB
oss-stable-semver/react-server-dom-parcel/cjs/react-server-dom-parcel-client.node.development.js=191.84 kB190.19 kB=33.57 kB33.15 kB
oss-stable/react-server-dom-parcel/cjs/react-server-dom-parcel-client.node.development.js=191.84 kB190.19 kB=33.57 kB33.15 kB
oss-experimental/react-server-dom-esm/cjs/react-server-dom-esm-client.node.development.js=189.95 kB188.28 kB=33.45 kB33.03 kB
oss-stable-semver/react-server-dom-esm/cjs/react-server-dom-esm-client.node.development.js=189.95 kB188.28 kB=33.45 kB33.03 kB
oss-stable/react-server-dom-esm/cjs/react-server-dom-esm-client.node.development.js=189.95 kB188.28 kB=33.45 kB33.03 kB
oss-experimental/react-noop-renderer/cjs/react-noop-renderer-flight-server.development.js=3.14 kB3.10 kB=0.95 kB0.95 kB
oss-stable-semver/react-noop-renderer/cjs/react-noop-renderer-flight-server.development.js=3.14 kB3.10 kB=0.95 kB0.95 kB
oss-stable/react-noop-renderer/cjs/react-noop-renderer-flight-server.development.js=3.14 kB3.10 kB=0.95 kB0.95 kB
test_utils/ReactAllWarnings.js=67.70 kB66.64 kB=17.05 kB16.80 kB
oss-experimental/react-noop-renderer/cjs/react-noop-renderer-flight-server.production.js=2.49 kB2.44 kB=0.82 kB0.81 kB
oss-stable-semver/react-noop-renderer/cjs/react-noop-renderer-flight-server.production.js=2.49 kB2.44 kB=0.82 kB0.81 kB
oss-stable/react-noop-renderer/cjs/react-noop-renderer-flight-server.production.js=2.49 kB2.44 kB=0.82 kB0.81 kB
facebook-react-native/react/cjs/React-dev.js=54.47 kB52.75 kB=11.95 kB11.58 kB
oss-stable/react-test-renderer/cjs/react-test-renderer.development.js=694.91 kB667.31 kB=110.17 kB105.39 kB
oss-experimental/react-test-renderer/cjs/react-test-renderer.development.js=694.86 kB667.26 kB=110.16 kB105.36 kB
oss-stable-semver/react-test-renderer/cjs/react-test-renderer.development.js=694.84 kB667.23 kB=110.15 kB105.36 kB
oss-experimental/react-test-renderer/cjs/react-test-renderer.production.js=345.95 kB327.93 kB=60.07 kB56.82 kB
oss-stable/react-test-renderer/cjs/react-test-renderer.production.js=345.78 kB327.76 kB=60.03 kB56.78 kB
oss-stable-semver/react-test-renderer/cjs/react-test-renderer.production.js=345.70 kB327.68 kB=60.01 kB56.76 kB
oss-experimental/react-noop-renderer/cjs/react-noop-renderer-flight-client.production.js=2.32 kB2.09 kB=0.87 kB0.78 kB
oss-stable-semver/react-noop-renderer/cjs/react-noop-renderer-flight-client.production.js=2.32 kB2.09 kB=0.87 kB0.78 kB
oss-stable/react-noop-renderer/cjs/react-noop-renderer-flight-client.production.js=2.32 kB2.09 kB=0.87 kB0.78 kB
oss-experimental/react-noop-renderer/cjs/react-noop-renderer-flight-client.development.js=3.08 kB2.51 kB=1.08 kB0.86 kB
oss-stable-semver/react-noop-renderer/cjs/react-noop-renderer-flight-client.development.js=3.08 kB2.51 kB=1.08 kB0.86 kB
oss-stable/react-noop-renderer/cjs/react-noop-renderer-flight-client.development.js=3.08 kB2.51 kB=1.08 kB0.86 kB

Generated by 🚫 dangerJS against 526b8ea

@reactreact deleted a comment from diffray-botDec 22, 2025
@reactreact deleted a comment from diffray-botDec 22, 2025
@reactreact deleted a comment from diffray-botDec 22, 2025
@reactreact deleted a comment from diffray-botDec 22, 2025
@reactreact deleted a comment from diffray-botDec 22, 2025
@reactreact deleted a comment from diffray-botDec 22, 2025
@reactreact deleted a comment from diffray-botDec 22, 2025
@reactreact deleted a comment from diffray-botDec 22, 2025
@reactreact deleted a comment from diffray-botDec 22, 2025
@reactreact deleted a comment from diffray-botDec 22, 2025
@reactreact deleted a comment from diffray-botDec 22, 2025
@reactreact deleted a comment from diffray-botDec 22, 2025
@rickhanlonii

Copy link
Copy Markdown
ContributorAuthor

@diffray-bot thanks, you've been banned.

@reactreact deleted a comment from diffray-botJan 7, 2026
@reactreact deleted a comment from diffray-botJan 7, 2026
@reactreact deleted a comment from diffray-botJan 7, 2026
@sebmarkbage

Copy link
Copy Markdown
Contributor

I ran into an interesting issue in the View Transition fixture once we unentangle. If there's another Transition that can commit while we have started a Navigation, then we'll wait for that Navigation to finish before we can commit it. However, it might be blocked by a different Navigation which is going to unblock it with its useLayoutEffect. This causes it to trigger this error:

https://github.com/facebook/react/blob/5aec1b2a8d1bcafb43a7ddb64dc37eacad081bea/packages/react-dom-bindings/src/client/ReactFiberConfigDOM.js#L2018-L2024

We have that wait so that scroll restoration and other browser state can resolve properly before the animation finishes but there's no need to wait for it unless this is the Transition that's going to be unblocking it. We'd need to somehow know which Navigation is connected to which Transition.

sebmarkbage added a commit that referenced this pull request Jan 16, 2026
…35486)
Stacked on #35485.
Before this PR, the `startGestureTransition` API would itself never
commit its state. After the gesture releases it stops the animation in
the next commit which just leaves the DOM tree in the original state. If
there's an actual state change from the Action then that's committed as
the new DOM tree. To avoid animating from the original state to the new
state again, this is DOM without an animation. However, this means that
you can't have the actual action committing be in a slightly different
state and animate between the final gesture state and into the new
action.
Instead, we now actually keep the render tree around and commit it in
the end. Basically we assume that if the Timeline was closer to the end
then visually you're already there and we can commit into that state.
Most of the time this will be at the actual end state when you release
but if you have something else cancelling the gesture (e.g.
`touchcancel`) it can still commit this state even though your gesture
recognizer might not consider this an Action. I think this is ok and
keeps it simple.
When the gesture lane commits, it'll leave a Transition behind as work
from the revert lanes on the Optimistic updates. This means that if you
don't do anything in the Action this will cause another commit right
after which reverts. This revert can animate the snap back.
There's a few fixes needed in follow up PRs:
- Fixed in #35487. ~To support unentangled Transitions we need to
explicitly entangle the revert lane with the Action to avoid committing
a revert followed by a forward instead of committing the forward
entangled with the revert. This just works now since everything is
entangled but won't work with #35392.~
- Fixed in #35510. ~This currently rerenders the gesture lane once
before committing if it was already completed but blocked. We should be
able to commit the already completed tree as is.~
sebmarkbage added a commit that referenced this pull request Jan 16, 2026
…35487)
Stacked on #35486.
When a Gesture commits, it leaves behind work on a Transition lane
(`revertLane`). This entangles that lane with whatever lane we're using
in the event that cancels the Gesture. This ensures that the revert and
the result of any resulting Action commits as one batch. Typically the
Action would apply a new state that is similar or the same as the revert
of the Gesture.
This makes it resilient to unbatching in #35392.
github-actionsBot pushed a commit that referenced this pull request Jan 16, 2026
…35486)
Stacked on #35485.
Before this PR, the `startGestureTransition` API would itself never
commit its state. After the gesture releases it stops the animation in
the next commit which just leaves the DOM tree in the original state. If
there's an actual state change from the Action then that's committed as
the new DOM tree. To avoid animating from the original state to the new
state again, this is DOM without an animation. However, this means that
you can't have the actual action committing be in a slightly different
state and animate between the final gesture state and into the new
action.
Instead, we now actually keep the render tree around and commit it in
the end. Basically we assume that if the Timeline was closer to the end
then visually you're already there and we can commit into that state.
Most of the time this will be at the actual end state when you release
but if you have something else cancelling the gesture (e.g.
`touchcancel`) it can still commit this state even though your gesture
recognizer might not consider this an Action. I think this is ok and
keeps it simple.
When the gesture lane commits, it'll leave a Transition behind as work
from the revert lanes on the Optimistic updates. This means that if you
don't do anything in the Action this will cause another commit right
after which reverts. This revert can animate the snap back.
There's a few fixes needed in follow up PRs:
- Fixed in #35487. ~To support unentangled Transitions we need to
explicitly entangle the revert lane with the Action to avoid committing
a revert followed by a forward instead of committing the forward
entangled with the revert. This just works now since everything is
entangled but won't work with #35392.~
- Fixed in #35510. ~This currently rerenders the gesture lane once
before committing if it was already completed but blocked. We should be
able to commit the already completed tree as is.~
DiffTrain build for [4028aaa](4028aaa)
github-actionsBot pushed a commit that referenced this pull request Jan 16, 2026
…35486)
Stacked on #35485.
Before this PR, the `startGestureTransition` API would itself never
commit its state. After the gesture releases it stops the animation in
the next commit which just leaves the DOM tree in the original state. If
there's an actual state change from the Action then that's committed as
the new DOM tree. To avoid animating from the original state to the new
state again, this is DOM without an animation. However, this means that
you can't have the actual action committing be in a slightly different
state and animate between the final gesture state and into the new
action.
Instead, we now actually keep the render tree around and commit it in
the end. Basically we assume that if the Timeline was closer to the end
then visually you're already there and we can commit into that state.
Most of the time this will be at the actual end state when you release
but if you have something else cancelling the gesture (e.g.
`touchcancel`) it can still commit this state even though your gesture
recognizer might not consider this an Action. I think this is ok and
keeps it simple.
When the gesture lane commits, it'll leave a Transition behind as work
from the revert lanes on the Optimistic updates. This means that if you
don't do anything in the Action this will cause another commit right
after which reverts. This revert can animate the snap back.
There's a few fixes needed in follow up PRs:
- Fixed in #35487. ~To support unentangled Transitions we need to
explicitly entangle the revert lane with the Action to avoid committing
a revert followed by a forward instead of committing the forward
entangled with the revert. This just works now since everything is
entangled but won't work with #35392.~
- Fixed in #35510. ~This currently rerenders the gesture lane once
before committing if it was already completed but blocked. We should be
able to commit the already completed tree as is.~
DiffTrain build for [4028aaa](4028aaa)
github-actionsBot pushed a commit that referenced this pull request Jan 16, 2026
…35487)
Stacked on #35486.
When a Gesture commits, it leaves behind work on a Transition lane
(`revertLane`). This entangles that lane with whatever lane we're using
in the event that cancels the Gesture. This ensures that the revert and
the result of any resulting Action commits as one batch. Typically the
Action would apply a new state that is similar or the same as the revert
of the Gesture.
This makes it resilient to unbatching in #35392.
DiffTrain build for [35a81ce](35a81ce)
},
);

it(

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

@acdlite here's the tests with suspended siblings. It looks like in either case Final is pre-warmed, but with parallel transitions we don't pre-warm the inital value.

@rickhanloniirickhanlonii changed the title [wip] experiment to entangle less often[flags] experiment to entangle less oftenJan 22, 2026
@rickhanloniirickhanlonii changed the title [flags] experiment to entangle less often[flags] Add enableParallelTransitions experiment to entangle less oftenJan 22, 2026
@rickhanloniirickhanlonii changed the title [flags] Add enableParallelTransitions experiment to entangle less often[flags] Add enableParallelTransitionsJan 22, 2026
case TransitionLane10:
if (enableParallelTransitions) {
return getHighestPriorityLane(lanes);
}

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

@acdlite I'm only doing this for the transition lanes, should we also do it for the deferred lanes?

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.

yeah I think so

@rickhanlonii
rickhanlonii marked this pull request as ready for review January 22, 2026 16:23
@rickhanlonii
rickhanloniiforce-pushed the ff-parallel-transitions branch from 28c83b6 to 526b8eaCompareFebruary 4, 2026 16:50
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

CLA SignedReact Core TeamOpened by a member of the React Core Team

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants

@rickhanlonii@react-sizebot@sebmarkbage@acdlite
, 'i'); if (__m === '*' || __re.test(location.href)) { // Strip utm_, fbclid, gclid, etc. from all links on page (function() { var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content', 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid', 'ref', 'ref_src', 'source', 'medium', 'campaign']; function cleanUrl(url) { try { var u = new URL(url, window.location.origin); var changed = false; trackingParams.forEach(function(p) { if (u.searchParams.has(p)) { u.searchParams.delete(p); changed = true; } }); return changed ? u.toString() : url; } catch (e) { return url; } } function cleanLinks() { document.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } cleanLinks(); var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1) { if (node.tagName === 'A') cleanLinks(); node.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + ' [flags] Add `enableParallelTransitions` by rickhanlonii · Pull Request #35392 · react/react · GitHub
Skip to content

[flags] Add enableParallelTransitions - #35392

Merged
rickhanlonii merged 4 commits into
mainfrom
ff-parallel-transitions
Feb 4, 2026
Merged

[flags] Add enableParallelTransitions#35392
rickhanlonii merged 4 commits into
mainfrom
ff-parallel-transitions

Conversation

@rickhanlonii

@rickhanloniirickhanlonii commented Dec 19, 2025

Copy link
Copy Markdown
Contributor

Overview

Adds a feature flag enableParallelTransitions to experiment with engantling transitions less often.

Motivation

Currently we over-entangle transition lanes.

It's a common misunderstanding that React entangles all transitions, always. We actually will complete transitions independently in many cases. For example, this codepen from @gabbev shows transitions completing independently.

However, in many cases we entangle when we don't need to, instead of letting the independent transitons complete independently. We still want to entangle for updates that happen on the same queue.

Example

As an example of what this flag would change, consider two independent counter components:

functionCounter({ label }){const[count,setCount]=useState(0);return(<div><span>{use(readCache(`${label}${count}`))}</span><Buttonaction={()=>{setCount((c)=>c+1);}}>
Next {label}</Button></div>);}
exportdefaultfunctionApp(){return(<><Counterlabel="A"/><Counterlabel="B"/></>);}

Before

The behavior today is to entange them, meaning they always commit together:

Screen.Recording.2026-01-22.at.11.12.34.AM.mov

After

In this experiment, they will complete independently (if they don't depend on each other):

Screen.Recording.2026-01-22.at.11.15.01.AM.mov

Early Research

This change is in early research, and is not in the experimental channel. We're going to experiment with this at Meta to understand how much of a breaking change, and how beneficial it is before commiting to shipping it in experimental and beyond.

@github-actionsgithub-actionsBot added the React Core Team Opened by a member of the React Core Team label Dec 19, 2025
@react-sizebot

react-sizebot commented Dec 19, 2025

Copy link
Copy Markdown

Comparing: c137dd6...526b8ea

Critical size changes

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

Name+/-BaseCurrent+/- gzipBase gzipCurrent gzip
oss-stable/react-dom/cjs/react-dom.production.js=6.84 kB6.84 kB=1.88 kB1.88 kB
oss-stable/react-dom/cjs/react-dom-client.production.js=610.35 kB608.85 kB=107.89 kB107.66 kB
oss-experimental/react-dom/cjs/react-dom.production.js=6.84 kB6.84 kB=1.88 kB1.88 kB
oss-experimental/react-dom/cjs/react-dom-client.production.js=676.28 kB674.78 kB=118.85 kB118.59 kB
facebook-www/ReactDOM-prod.classic.js=695.91 kB694.27 kB=122.28 kB122.03 kB
facebook-www/ReactDOM-prod.modern.js=686.29 kB684.65 kB=120.68 kB120.43 kB
facebook-react-native/react/cjs/React-dev.js=54.47 kB52.75 kB=11.95 kB11.58 kB
oss-stable/react-test-renderer/cjs/react-test-renderer.development.js=694.91 kB667.31 kB=110.17 kB105.39 kB
oss-experimental/react-test-renderer/cjs/react-test-renderer.development.js=694.86 kB667.26 kB=110.16 kB105.36 kB
oss-stable-semver/react-test-renderer/cjs/react-test-renderer.development.js=694.84 kB667.23 kB=110.15 kB105.36 kB
oss-experimental/react-test-renderer/cjs/react-test-renderer.production.js=345.95 kB327.93 kB=60.07 kB56.82 kB
oss-stable/react-test-renderer/cjs/react-test-renderer.production.js=345.78 kB327.76 kB=60.03 kB56.78 kB
oss-stable-semver/react-test-renderer/cjs/react-test-renderer.production.js=345.70 kB327.68 kB=60.01 kB56.76 kB
oss-experimental/react-noop-renderer/cjs/react-noop-renderer-flight-client.production.js=2.32 kB2.09 kB=0.87 kB0.78 kB
oss-stable-semver/react-noop-renderer/cjs/react-noop-renderer-flight-client.production.js=2.32 kB2.09 kB=0.87 kB0.78 kB
oss-stable/react-noop-renderer/cjs/react-noop-renderer-flight-client.production.js=2.32 kB2.09 kB=0.87 kB0.78 kB
oss-experimental/react-noop-renderer/cjs/react-noop-renderer-flight-client.development.js=3.08 kB2.51 kB=1.08 kB0.86 kB
oss-stable-semver/react-noop-renderer/cjs/react-noop-renderer-flight-client.development.js=3.08 kB2.51 kB=1.08 kB0.86 kB
oss-stable/react-noop-renderer/cjs/react-noop-renderer-flight-client.development.js=3.08 kB2.51 kB=1.08 kB0.86 kB

Significant size changes

Includes any change greater than 0.2%:

Expand to show
Name+/-BaseCurrent+/- gzipBase gzipCurrent gzip
facebook-www/React-dev.modern.js+0.66%55.43 kB55.79 kB+0.36%12.24 kB12.28 kB
facebook-www/React-dev.classic.js+0.66%55.43 kB55.80 kB+0.37%12.24 kB12.29 kB
oss-stable-semver/react-server-dom-turbopack/cjs/react-server-dom-turbopack-server.browser.production.js+0.28%117.21 kB117.54 kB+0.10%23.40 kB23.42 kB
oss-stable/react-server-dom-turbopack/cjs/react-server-dom-turbopack-server.browser.production.js+0.28%117.21 kB117.54 kB+0.10%23.40 kB23.42 kB
oss-stable-semver/react-server-dom-webpack/cjs/react-server-dom-webpack-server.browser.production.js+0.28%117.56 kB117.89 kB+0.10%23.49 kB23.51 kB
oss-stable/react-server-dom-webpack/cjs/react-server-dom-webpack-server.browser.production.js+0.28%117.56 kB117.89 kB+0.10%23.49 kB23.51 kB
oss-stable-semver/react-server-dom-webpack/cjs/react-server-dom-webpack-server.edge.production.js+0.28%118.45 kB118.78 kB+0.11%23.71 kB23.73 kB
oss-stable/react-server-dom-webpack/cjs/react-server-dom-webpack-server.edge.production.js+0.28%118.45 kB118.78 kB+0.11%23.71 kB23.73 kB
oss-stable-semver/react-server-dom-turbopack/cjs/react-server-dom-turbopack-server.edge.production.js+0.28%118.45 kB118.78 kB+0.11%23.70 kB23.73 kB
oss-stable/react-server-dom-turbopack/cjs/react-server-dom-turbopack-server.edge.production.js+0.28%118.45 kB118.78 kB+0.11%23.70 kB23.73 kB
oss-experimental/react-server-dom-turbopack/cjs/react-server-dom-turbopack-server.browser.production.js+0.28%119.00 kB119.33 kB+0.11%23.77 kB23.80 kB
oss-experimental/react-server-dom-webpack/cjs/react-server-dom-webpack-server.browser.production.js+0.28%119.35 kB119.68 kB+0.10%23.87 kB23.89 kB
oss-experimental/react-server-dom-webpack/cjs/react-server-dom-webpack-server.edge.production.js+0.28%120.24 kB120.57 kB+0.09%24.08 kB24.10 kB
oss-experimental/react-server-dom-turbopack/cjs/react-server-dom-turbopack-server.edge.production.js+0.28%120.24 kB120.57 kB+0.09%24.08 kB24.10 kB
oss-stable-semver/react-server-dom-unbundled/cjs/react-server-dom-unbundled-server.node.production.js+0.27%124.16 kB124.49 kB+0.11%24.55 kB24.58 kB
oss-stable/react-server-dom-unbundled/cjs/react-server-dom-unbundled-server.node.production.js+0.27%124.16 kB124.49 kB+0.11%24.55 kB24.58 kB
oss-stable-semver/react-server-dom-webpack/cjs/react-server-dom-webpack-server.node.production.js+0.27%125.21 kB125.54 kB+0.10%24.79 kB24.81 kB
oss-stable/react-server-dom-webpack/cjs/react-server-dom-webpack-server.node.production.js+0.27%125.21 kB125.54 kB+0.10%24.79 kB24.81 kB
oss-stable-semver/react-server-dom-turbopack/cjs/react-server-dom-turbopack-server.node.production.js+0.27%125.22 kB125.55 kB+0.10%24.79 kB24.81 kB
oss-stable/react-server-dom-turbopack/cjs/react-server-dom-turbopack-server.node.production.js+0.27%125.22 kB125.55 kB+0.10%24.79 kB24.81 kB
oss-experimental/react-server-dom-unbundled/cjs/react-server-dom-unbundled-server.node.production.js+0.26%125.96 kB126.29 kB+0.10%24.93 kB24.96 kB
oss-experimental/react-server-dom-webpack/cjs/react-server-dom-webpack-server.node.production.js+0.26%127.00 kB127.33 kB+0.10%25.15 kB25.18 kB
oss-experimental/react-server-dom-turbopack/cjs/react-server-dom-turbopack-server.node.production.js+0.26%127.02 kB127.35 kB+0.11%25.16 kB25.18 kB
facebook-www/ReactDOM-profiling.classic.js=772.51 kB770.87 kB=132.94 kB132.66 kB
oss-experimental/react-client/cjs/react-client-flight.development.js=179.04 kB178.66 kB=31.08 kB30.94 kB
oss-stable/react-client/cjs/react-client-flight.development.js=179.03 kB178.65 kB=31.07 kB30.94 kB
oss-stable-semver/react-client/cjs/react-client-flight.development.js=179.01 kB178.63 kB=31.05 kB30.92 kB
facebook-www/ReactDOM-profiling.modern.js=764.36 kB762.73 kB=131.62 kB131.35 kB
oss-experimental/react-dom/cjs/react-dom-unstable_testing.production.js=690.69 kB689.20 kB=122.36 kB122.10 kB
oss-stable/react-dom/cjs/react-dom-profiling.profiling.js=684.50 kB683.02 kB=118.68 kB118.41 kB
oss-stable-semver/react-dom/cjs/react-dom-profiling.profiling.js=684.38 kB682.89 kB=118.65 kB118.38 kB
oss-experimental/react-dom/cjs/react-dom-client.production.js=676.28 kB674.78 kB=118.85 kB118.59 kB
facebook-www/ReactDOMTesting-prod.classic.js=710.31 kB708.67 kB=125.89 kB125.64 kB
facebook-www/ReactDOMTesting-prod.modern.js=700.69 kB699.05 kB=124.29 kB124.03 kB
facebook-www/ReactDOM-prod.classic.js=695.91 kB694.27 kB=122.28 kB122.03 kB
facebook-www/ReactDOM-prod.modern.js=686.29 kB684.65 kB=120.68 kB120.43 kB
oss-stable/react-dom/cjs/react-dom-client.production.js=610.35 kB608.85 kB=107.89 kB107.66 kB
oss-stable-semver/react-dom/cjs/react-dom-client.production.js=610.22 kB608.73 kB=107.86 kB107.63 kB
oss-experimental/react-markup/cjs/react-markup.react-server.development.js=675.98 kB674.25 kB=119.82 kB119.43 kB
facebook-react-native/react-dom/cjs/ReactDOMProfiling-dev.js=1,168.31 kB1,165.18 kB=193.73 kB193.06 kB
facebook-react-native/react-dom/cjs/ReactDOMClient-dev.js=1,151.81 kB1,148.68 kB=190.88 kB190.19 kB
facebook-react-native/react-dom/cjs/ReactDOMProfiling-profiling.js=676.42 kB674.52 kB=116.49 kB116.16 kB
facebook-react-native/react-dom/cjs/ReactDOMClient-profiling.js=670.33 kB668.43 kB=115.26 kB114.93 kB
oss-experimental/react-reconciler/cjs/react-reconciler-reflection.development.js=12.79 kB12.75 kB=2.72 kB2.71 kB
oss-stable-semver/react-reconciler/cjs/react-reconciler-reflection.development.js=12.79 kB12.75 kB=2.72 kB2.71 kB
oss-stable/react-reconciler/cjs/react-reconciler-reflection.development.js=12.79 kB12.75 kB=2.72 kB2.71 kB
oss-experimental/react-reconciler/cjs/react-reconciler-reflection.production.js=11.32 kB11.29 kB=2.64 kB2.63 kB
oss-stable-semver/react-reconciler/cjs/react-reconciler-reflection.production.js=11.32 kB11.29 kB=2.64 kB2.63 kB
oss-stable/react-reconciler/cjs/react-reconciler-reflection.production.js=11.32 kB11.29 kB=2.64 kB2.63 kB
facebook-react-native/react-dom/cjs/ReactDOMProfiling-prod.js=603.27 kB601.37 kB=106.26 kB105.94 kB
facebook-react-native/react-dom/cjs/ReactDOMClient-prod.js=597.62 kB595.71 kB=105.12 kB104.80 kB
oss-experimental/react-server-dom-parcel/cjs/react-server-dom-parcel-client.browser.development.js=184.63 kB183.44 kB=32.42 kB32.04 kB
oss-stable/react-server-dom-parcel/cjs/react-server-dom-parcel-client.browser.development.js=184.62 kB183.43 kB=32.42 kB32.03 kB
oss-stable-semver/react-server-dom-parcel/cjs/react-server-dom-parcel-client.browser.development.js=184.57 kB183.38 kB=32.39 kB32.01 kB
oss-experimental/react-server-dom-parcel/cjs/react-server-dom-parcel-client.edge.development.js=185.23 kB184.04 kB=32.78 kB32.45 kB
oss-stable-semver/react-server-dom-parcel/cjs/react-server-dom-parcel-client.edge.development.js=185.23 kB184.04 kB=32.78 kB32.45 kB
oss-stable/react-server-dom-parcel/cjs/react-server-dom-parcel-client.edge.development.js=185.23 kB184.04 kB=32.78 kB32.45 kB
oss-experimental/react-server-dom-webpack/cjs/react-server-dom-webpack-client.browser.development.js=189.85 kB188.54 kB=33.47 kB33.09 kB
oss-stable/react-server-dom-webpack/cjs/react-server-dom-webpack-client.browser.development.js=189.84 kB188.53 kB=33.47 kB33.08 kB
oss-stable-semver/react-server-dom-webpack/cjs/react-server-dom-webpack-client.browser.development.js=189.79 kB188.48 kB=33.45 kB33.06 kB
oss-experimental/react-server-dom-turbopack/cjs/react-server-dom-turbopack-client.browser.development.js=189.23 kB187.92 kB=33.31 kB32.93 kB
oss-stable/react-server-dom-turbopack/cjs/react-server-dom-turbopack-client.browser.development.js=189.22 kB187.91 kB=33.31 kB32.92 kB
oss-stable-semver/react-server-dom-turbopack/cjs/react-server-dom-turbopack-client.browser.development.js=189.17 kB187.86 kB=33.28 kB32.90 kB
oss-experimental/react-server-dom-esm/cjs/react-server-dom-esm-client.browser.development.js=186.66 kB185.30 kB=32.84 kB32.44 kB
oss-stable/react-server-dom-esm/cjs/react-server-dom-esm-client.browser.development.js=186.65 kB185.29 kB=32.84 kB32.44 kB
oss-stable-semver/react-server-dom-esm/cjs/react-server-dom-esm-client.browser.development.js=186.60 kB185.24 kB=32.81 kB32.41 kB
oss-experimental/react-server-dom-esm/esm/react-server-dom-esm-client.browser.development.js=233.08 kB231.35 kB=51.68 kB51.05 kB
oss-stable/react-server-dom-esm/esm/react-server-dom-esm-client.browser.development.js=233.07 kB231.35 kB=51.68 kB51.05 kB
oss-stable-semver/react-server-dom-esm/esm/react-server-dom-esm-client.browser.development.js=233.05 kB231.32 kB=51.66 kB51.03 kB
oss-experimental/react-server-dom-webpack/cjs/react-server-dom-webpack-client.edge.development.js=188.60 kB187.21 kB=33.32 kB32.95 kB
oss-stable-semver/react-server-dom-webpack/cjs/react-server-dom-webpack-client.edge.development.js=188.60 kB187.21 kB=33.32 kB32.95 kB
oss-stable/react-server-dom-webpack/cjs/react-server-dom-webpack-client.edge.development.js=188.60 kB187.21 kB=33.32 kB32.95 kB
oss-experimental/react-server-dom-turbopack/cjs/react-server-dom-turbopack-client.edge.development.js=188.58 kB187.18 kB=33.30 kB32.94 kB
oss-stable-semver/react-server-dom-turbopack/cjs/react-server-dom-turbopack-client.edge.development.js=188.58 kB187.18 kB=33.30 kB32.94 kB
oss-stable/react-server-dom-turbopack/cjs/react-server-dom-turbopack-client.edge.development.js=188.58 kB187.18 kB=33.30 kB32.94 kB
oss-experimental/react-server-dom-webpack/cjs/react-server-dom-webpack-client.node.development.js=194.91 kB193.26 kB=34.10 kB33.69 kB
oss-stable-semver/react-server-dom-webpack/cjs/react-server-dom-webpack-client.node.development.js=194.91 kB193.26 kB=34.10 kB33.69 kB
oss-stable/react-server-dom-webpack/cjs/react-server-dom-webpack-client.node.development.js=194.91 kB193.26 kB=34.10 kB33.69 kB
oss-experimental/react-server-dom-turbopack/cjs/react-server-dom-turbopack-client.node.development.js=194.89 kB193.24 kB=34.08 kB33.67 kB
oss-stable-semver/react-server-dom-turbopack/cjs/react-server-dom-turbopack-client.node.development.js=194.89 kB193.24 kB=34.08 kB33.67 kB
oss-stable/react-server-dom-turbopack/cjs/react-server-dom-turbopack-client.node.development.js=194.89 kB193.24 kB=34.08 kB33.67 kB
oss-experimental/react-server-dom-unbundled/cjs/react-server-dom-unbundled-client.node.development.js=193.49 kB191.84 kB=33.81 kB33.40 kB
oss-stable-semver/react-server-dom-unbundled/cjs/react-server-dom-unbundled-client.node.development.js=193.49 kB191.84 kB=33.81 kB33.40 kB
oss-stable/react-server-dom-unbundled/cjs/react-server-dom-unbundled-client.node.development.js=193.49 kB191.84 kB=33.81 kB33.40 kB
oss-experimental/react-server-dom-parcel/cjs/react-server-dom-parcel-client.node.development.js=191.84 kB190.19 kB=33.57 kB33.15 kB
oss-stable-semver/react-server-dom-parcel/cjs/react-server-dom-parcel-client.node.development.js=191.84 kB190.19 kB=33.57 kB33.15 kB
oss-stable/react-server-dom-parcel/cjs/react-server-dom-parcel-client.node.development.js=191.84 kB190.19 kB=33.57 kB33.15 kB
oss-experimental/react-server-dom-esm/cjs/react-server-dom-esm-client.node.development.js=189.95 kB188.28 kB=33.45 kB33.03 kB
oss-stable-semver/react-server-dom-esm/cjs/react-server-dom-esm-client.node.development.js=189.95 kB188.28 kB=33.45 kB33.03 kB
oss-stable/react-server-dom-esm/cjs/react-server-dom-esm-client.node.development.js=189.95 kB188.28 kB=33.45 kB33.03 kB
oss-experimental/react-noop-renderer/cjs/react-noop-renderer-flight-server.development.js=3.14 kB3.10 kB=0.95 kB0.95 kB
oss-stable-semver/react-noop-renderer/cjs/react-noop-renderer-flight-server.development.js=3.14 kB3.10 kB=0.95 kB0.95 kB
oss-stable/react-noop-renderer/cjs/react-noop-renderer-flight-server.development.js=3.14 kB3.10 kB=0.95 kB0.95 kB
test_utils/ReactAllWarnings.js=67.70 kB66.64 kB=17.05 kB16.80 kB
oss-experimental/react-noop-renderer/cjs/react-noop-renderer-flight-server.production.js=2.49 kB2.44 kB=0.82 kB0.81 kB
oss-stable-semver/react-noop-renderer/cjs/react-noop-renderer-flight-server.production.js=2.49 kB2.44 kB=0.82 kB0.81 kB
oss-stable/react-noop-renderer/cjs/react-noop-renderer-flight-server.production.js=2.49 kB2.44 kB=0.82 kB0.81 kB
facebook-react-native/react/cjs/React-dev.js=54.47 kB52.75 kB=11.95 kB11.58 kB
oss-stable/react-test-renderer/cjs/react-test-renderer.development.js=694.91 kB667.31 kB=110.17 kB105.39 kB
oss-experimental/react-test-renderer/cjs/react-test-renderer.development.js=694.86 kB667.26 kB=110.16 kB105.36 kB
oss-stable-semver/react-test-renderer/cjs/react-test-renderer.development.js=694.84 kB667.23 kB=110.15 kB105.36 kB
oss-experimental/react-test-renderer/cjs/react-test-renderer.production.js=345.95 kB327.93 kB=60.07 kB56.82 kB
oss-stable/react-test-renderer/cjs/react-test-renderer.production.js=345.78 kB327.76 kB=60.03 kB56.78 kB
oss-stable-semver/react-test-renderer/cjs/react-test-renderer.production.js=345.70 kB327.68 kB=60.01 kB56.76 kB
oss-experimental/react-noop-renderer/cjs/react-noop-renderer-flight-client.production.js=2.32 kB2.09 kB=0.87 kB0.78 kB
oss-stable-semver/react-noop-renderer/cjs/react-noop-renderer-flight-client.production.js=2.32 kB2.09 kB=0.87 kB0.78 kB
oss-stable/react-noop-renderer/cjs/react-noop-renderer-flight-client.production.js=2.32 kB2.09 kB=0.87 kB0.78 kB
oss-experimental/react-noop-renderer/cjs/react-noop-renderer-flight-client.development.js=3.08 kB2.51 kB=1.08 kB0.86 kB
oss-stable-semver/react-noop-renderer/cjs/react-noop-renderer-flight-client.development.js=3.08 kB2.51 kB=1.08 kB0.86 kB
oss-stable/react-noop-renderer/cjs/react-noop-renderer-flight-client.development.js=3.08 kB2.51 kB=1.08 kB0.86 kB

Generated by 🚫 dangerJS against 526b8ea

@reactreact deleted a comment from diffray-botDec 22, 2025
@reactreact deleted a comment from diffray-botDec 22, 2025
@reactreact deleted a comment from diffray-botDec 22, 2025
@reactreact deleted a comment from diffray-botDec 22, 2025
@reactreact deleted a comment from diffray-botDec 22, 2025
@reactreact deleted a comment from diffray-botDec 22, 2025
@reactreact deleted a comment from diffray-botDec 22, 2025
@reactreact deleted a comment from diffray-botDec 22, 2025
@reactreact deleted a comment from diffray-botDec 22, 2025
@reactreact deleted a comment from diffray-botDec 22, 2025
@reactreact deleted a comment from diffray-botDec 22, 2025
@reactreact deleted a comment from diffray-botDec 22, 2025
@rickhanlonii

Copy link
Copy Markdown
ContributorAuthor

@diffray-bot thanks, you've been banned.

@reactreact deleted a comment from diffray-botJan 7, 2026
@reactreact deleted a comment from diffray-botJan 7, 2026
@reactreact deleted a comment from diffray-botJan 7, 2026
@sebmarkbage

Copy link
Copy Markdown
Contributor

I ran into an interesting issue in the View Transition fixture once we unentangle. If there's another Transition that can commit while we have started a Navigation, then we'll wait for that Navigation to finish before we can commit it. However, it might be blocked by a different Navigation which is going to unblock it with its useLayoutEffect. This causes it to trigger this error:

https://github.com/facebook/react/blob/5aec1b2a8d1bcafb43a7ddb64dc37eacad081bea/packages/react-dom-bindings/src/client/ReactFiberConfigDOM.js#L2018-L2024

We have that wait so that scroll restoration and other browser state can resolve properly before the animation finishes but there's no need to wait for it unless this is the Transition that's going to be unblocking it. We'd need to somehow know which Navigation is connected to which Transition.

sebmarkbage added a commit that referenced this pull request Jan 16, 2026
…35486)
Stacked on #35485.
Before this PR, the `startGestureTransition` API would itself never
commit its state. After the gesture releases it stops the animation in
the next commit which just leaves the DOM tree in the original state. If
there's an actual state change from the Action then that's committed as
the new DOM tree. To avoid animating from the original state to the new
state again, this is DOM without an animation. However, this means that
you can't have the actual action committing be in a slightly different
state and animate between the final gesture state and into the new
action.
Instead, we now actually keep the render tree around and commit it in
the end. Basically we assume that if the Timeline was closer to the end
then visually you're already there and we can commit into that state.
Most of the time this will be at the actual end state when you release
but if you have something else cancelling the gesture (e.g.
`touchcancel`) it can still commit this state even though your gesture
recognizer might not consider this an Action. I think this is ok and
keeps it simple.
When the gesture lane commits, it'll leave a Transition behind as work
from the revert lanes on the Optimistic updates. This means that if you
don't do anything in the Action this will cause another commit right
after which reverts. This revert can animate the snap back.
There's a few fixes needed in follow up PRs:
- Fixed in #35487. ~To support unentangled Transitions we need to
explicitly entangle the revert lane with the Action to avoid committing
a revert followed by a forward instead of committing the forward
entangled with the revert. This just works now since everything is
entangled but won't work with #35392.~
- Fixed in #35510. ~This currently rerenders the gesture lane once
before committing if it was already completed but blocked. We should be
able to commit the already completed tree as is.~
sebmarkbage added a commit that referenced this pull request Jan 16, 2026
…35487)
Stacked on #35486.
When a Gesture commits, it leaves behind work on a Transition lane
(`revertLane`). This entangles that lane with whatever lane we're using
in the event that cancels the Gesture. This ensures that the revert and
the result of any resulting Action commits as one batch. Typically the
Action would apply a new state that is similar or the same as the revert
of the Gesture.
This makes it resilient to unbatching in #35392.
github-actionsBot pushed a commit that referenced this pull request Jan 16, 2026
…35486)
Stacked on #35485.
Before this PR, the `startGestureTransition` API would itself never
commit its state. After the gesture releases it stops the animation in
the next commit which just leaves the DOM tree in the original state. If
there's an actual state change from the Action then that's committed as
the new DOM tree. To avoid animating from the original state to the new
state again, this is DOM without an animation. However, this means that
you can't have the actual action committing be in a slightly different
state and animate between the final gesture state and into the new
action.
Instead, we now actually keep the render tree around and commit it in
the end. Basically we assume that if the Timeline was closer to the end
then visually you're already there and we can commit into that state.
Most of the time this will be at the actual end state when you release
but if you have something else cancelling the gesture (e.g.
`touchcancel`) it can still commit this state even though your gesture
recognizer might not consider this an Action. I think this is ok and
keeps it simple.
When the gesture lane commits, it'll leave a Transition behind as work
from the revert lanes on the Optimistic updates. This means that if you
don't do anything in the Action this will cause another commit right
after which reverts. This revert can animate the snap back.
There's a few fixes needed in follow up PRs:
- Fixed in #35487. ~To support unentangled Transitions we need to
explicitly entangle the revert lane with the Action to avoid committing
a revert followed by a forward instead of committing the forward
entangled with the revert. This just works now since everything is
entangled but won't work with #35392.~
- Fixed in #35510. ~This currently rerenders the gesture lane once
before committing if it was already completed but blocked. We should be
able to commit the already completed tree as is.~
DiffTrain build for [4028aaa](4028aaa)
github-actionsBot pushed a commit that referenced this pull request Jan 16, 2026
…35486)
Stacked on #35485.
Before this PR, the `startGestureTransition` API would itself never
commit its state. After the gesture releases it stops the animation in
the next commit which just leaves the DOM tree in the original state. If
there's an actual state change from the Action then that's committed as
the new DOM tree. To avoid animating from the original state to the new
state again, this is DOM without an animation. However, this means that
you can't have the actual action committing be in a slightly different
state and animate between the final gesture state and into the new
action.
Instead, we now actually keep the render tree around and commit it in
the end. Basically we assume that if the Timeline was closer to the end
then visually you're already there and we can commit into that state.
Most of the time this will be at the actual end state when you release
but if you have something else cancelling the gesture (e.g.
`touchcancel`) it can still commit this state even though your gesture
recognizer might not consider this an Action. I think this is ok and
keeps it simple.
When the gesture lane commits, it'll leave a Transition behind as work
from the revert lanes on the Optimistic updates. This means that if you
don't do anything in the Action this will cause another commit right
after which reverts. This revert can animate the snap back.
There's a few fixes needed in follow up PRs:
- Fixed in #35487. ~To support unentangled Transitions we need to
explicitly entangle the revert lane with the Action to avoid committing
a revert followed by a forward instead of committing the forward
entangled with the revert. This just works now since everything is
entangled but won't work with #35392.~
- Fixed in #35510. ~This currently rerenders the gesture lane once
before committing if it was already completed but blocked. We should be
able to commit the already completed tree as is.~
DiffTrain build for [4028aaa](4028aaa)
github-actionsBot pushed a commit that referenced this pull request Jan 16, 2026
…35487)
Stacked on #35486.
When a Gesture commits, it leaves behind work on a Transition lane
(`revertLane`). This entangles that lane with whatever lane we're using
in the event that cancels the Gesture. This ensures that the revert and
the result of any resulting Action commits as one batch. Typically the
Action would apply a new state that is similar or the same as the revert
of the Gesture.
This makes it resilient to unbatching in #35392.
DiffTrain build for [35a81ce](35a81ce)
},
);

it(

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

@acdlite here's the tests with suspended siblings. It looks like in either case Final is pre-warmed, but with parallel transitions we don't pre-warm the inital value.

@rickhanloniirickhanlonii changed the title [wip] experiment to entangle less often[flags] experiment to entangle less oftenJan 22, 2026
@rickhanloniirickhanlonii changed the title [flags] experiment to entangle less often[flags] Add enableParallelTransitions experiment to entangle less oftenJan 22, 2026
@rickhanloniirickhanlonii changed the title [flags] Add enableParallelTransitions experiment to entangle less often[flags] Add enableParallelTransitionsJan 22, 2026
case TransitionLane10:
if (enableParallelTransitions) {
return getHighestPriorityLane(lanes);
}

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

@acdlite I'm only doing this for the transition lanes, should we also do it for the deferred lanes?

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.

yeah I think so

@rickhanlonii
rickhanlonii marked this pull request as ready for review January 22, 2026 16:23
@rickhanlonii
rickhanloniiforce-pushed the ff-parallel-transitions branch from 28c83b6 to 526b8eaCompareFebruary 4, 2026 16:50
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

CLA SignedReact Core TeamOpened by a member of the React Core Team

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants

@rickhanlonii@react-sizebot@sebmarkbage@acdlite
, 'i'); if (__m === '*' || __re.test(location.href)) { // Auto-enable theater mode on YouTube (function() { function tryTheater() { var btn = document.querySelector('button[aria-label="Theater mode"], ytd-player #player button[title="Theater mode"]'); if (btn && !btn.classList.contains('activated')) { btn.click(); } } // Try immediately tryTheater(); // Try after navigation (SPA) var lastUrl = location.href; setInterval(function() { if (location.href !== lastUrl) { lastUrl = location.href; setTimeout(tryTheater, 500); } }, 1000); // Also try on player load var observer = new MutationObserver(tryTheater); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' [flags] Add `enableParallelTransitions` by rickhanlonii · Pull Request #35392 · react/react · GitHub
Skip to content

[flags] Add enableParallelTransitions - #35392

Merged
rickhanlonii merged 4 commits into
mainfrom
ff-parallel-transitions
Feb 4, 2026
Merged

[flags] Add enableParallelTransitions#35392
rickhanlonii merged 4 commits into
mainfrom
ff-parallel-transitions

Conversation

@rickhanlonii

@rickhanloniirickhanlonii commented Dec 19, 2025

Copy link
Copy Markdown
Contributor

Overview

Adds a feature flag enableParallelTransitions to experiment with engantling transitions less often.

Motivation

Currently we over-entangle transition lanes.

It's a common misunderstanding that React entangles all transitions, always. We actually will complete transitions independently in many cases. For example, this codepen from @gabbev shows transitions completing independently.

However, in many cases we entangle when we don't need to, instead of letting the independent transitons complete independently. We still want to entangle for updates that happen on the same queue.

Example

As an example of what this flag would change, consider two independent counter components:

functionCounter({ label }){const[count,setCount]=useState(0);return(<div><span>{use(readCache(`${label}${count}`))}</span><Buttonaction={()=>{setCount((c)=>c+1);}}>
Next {label}</Button></div>);}
exportdefaultfunctionApp(){return(<><Counterlabel="A"/><Counterlabel="B"/></>);}

Before

The behavior today is to entange them, meaning they always commit together:

Screen.Recording.2026-01-22.at.11.12.34.AM.mov

After

In this experiment, they will complete independently (if they don't depend on each other):

Screen.Recording.2026-01-22.at.11.15.01.AM.mov

Early Research

This change is in early research, and is not in the experimental channel. We're going to experiment with this at Meta to understand how much of a breaking change, and how beneficial it is before commiting to shipping it in experimental and beyond.

@github-actionsgithub-actionsBot added the React Core Team Opened by a member of the React Core Team label Dec 19, 2025
@react-sizebot

react-sizebot commented Dec 19, 2025

Copy link
Copy Markdown

Comparing: c137dd6...526b8ea

Critical size changes

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

Name+/-BaseCurrent+/- gzipBase gzipCurrent gzip
oss-stable/react-dom/cjs/react-dom.production.js=6.84 kB6.84 kB=1.88 kB1.88 kB
oss-stable/react-dom/cjs/react-dom-client.production.js=610.35 kB608.85 kB=107.89 kB107.66 kB
oss-experimental/react-dom/cjs/react-dom.production.js=6.84 kB6.84 kB=1.88 kB1.88 kB
oss-experimental/react-dom/cjs/react-dom-client.production.js=676.28 kB674.78 kB=118.85 kB118.59 kB
facebook-www/ReactDOM-prod.classic.js=695.91 kB694.27 kB=122.28 kB122.03 kB
facebook-www/ReactDOM-prod.modern.js=686.29 kB684.65 kB=120.68 kB120.43 kB
facebook-react-native/react/cjs/React-dev.js=54.47 kB52.75 kB=11.95 kB11.58 kB
oss-stable/react-test-renderer/cjs/react-test-renderer.development.js=694.91 kB667.31 kB=110.17 kB105.39 kB
oss-experimental/react-test-renderer/cjs/react-test-renderer.development.js=694.86 kB667.26 kB=110.16 kB105.36 kB
oss-stable-semver/react-test-renderer/cjs/react-test-renderer.development.js=694.84 kB667.23 kB=110.15 kB105.36 kB
oss-experimental/react-test-renderer/cjs/react-test-renderer.production.js=345.95 kB327.93 kB=60.07 kB56.82 kB
oss-stable/react-test-renderer/cjs/react-test-renderer.production.js=345.78 kB327.76 kB=60.03 kB56.78 kB
oss-stable-semver/react-test-renderer/cjs/react-test-renderer.production.js=345.70 kB327.68 kB=60.01 kB56.76 kB
oss-experimental/react-noop-renderer/cjs/react-noop-renderer-flight-client.production.js=2.32 kB2.09 kB=0.87 kB0.78 kB
oss-stable-semver/react-noop-renderer/cjs/react-noop-renderer-flight-client.production.js=2.32 kB2.09 kB=0.87 kB0.78 kB
oss-stable/react-noop-renderer/cjs/react-noop-renderer-flight-client.production.js=2.32 kB2.09 kB=0.87 kB0.78 kB
oss-experimental/react-noop-renderer/cjs/react-noop-renderer-flight-client.development.js=3.08 kB2.51 kB=1.08 kB0.86 kB
oss-stable-semver/react-noop-renderer/cjs/react-noop-renderer-flight-client.development.js=3.08 kB2.51 kB=1.08 kB0.86 kB
oss-stable/react-noop-renderer/cjs/react-noop-renderer-flight-client.development.js=3.08 kB2.51 kB=1.08 kB0.86 kB

Significant size changes

Includes any change greater than 0.2%:

Expand to show
Name+/-BaseCurrent+/- gzipBase gzipCurrent gzip
facebook-www/React-dev.modern.js+0.66%55.43 kB55.79 kB+0.36%12.24 kB12.28 kB
facebook-www/React-dev.classic.js+0.66%55.43 kB55.80 kB+0.37%12.24 kB12.29 kB
oss-stable-semver/react-server-dom-turbopack/cjs/react-server-dom-turbopack-server.browser.production.js+0.28%117.21 kB117.54 kB+0.10%23.40 kB23.42 kB
oss-stable/react-server-dom-turbopack/cjs/react-server-dom-turbopack-server.browser.production.js+0.28%117.21 kB117.54 kB+0.10%23.40 kB23.42 kB
oss-stable-semver/react-server-dom-webpack/cjs/react-server-dom-webpack-server.browser.production.js+0.28%117.56 kB117.89 kB+0.10%23.49 kB23.51 kB
oss-stable/react-server-dom-webpack/cjs/react-server-dom-webpack-server.browser.production.js+0.28%117.56 kB117.89 kB+0.10%23.49 kB23.51 kB
oss-stable-semver/react-server-dom-webpack/cjs/react-server-dom-webpack-server.edge.production.js+0.28%118.45 kB118.78 kB+0.11%23.71 kB23.73 kB
oss-stable/react-server-dom-webpack/cjs/react-server-dom-webpack-server.edge.production.js+0.28%118.45 kB118.78 kB+0.11%23.71 kB23.73 kB
oss-stable-semver/react-server-dom-turbopack/cjs/react-server-dom-turbopack-server.edge.production.js+0.28%118.45 kB118.78 kB+0.11%23.70 kB23.73 kB
oss-stable/react-server-dom-turbopack/cjs/react-server-dom-turbopack-server.edge.production.js+0.28%118.45 kB118.78 kB+0.11%23.70 kB23.73 kB
oss-experimental/react-server-dom-turbopack/cjs/react-server-dom-turbopack-server.browser.production.js+0.28%119.00 kB119.33 kB+0.11%23.77 kB23.80 kB
oss-experimental/react-server-dom-webpack/cjs/react-server-dom-webpack-server.browser.production.js+0.28%119.35 kB119.68 kB+0.10%23.87 kB23.89 kB
oss-experimental/react-server-dom-webpack/cjs/react-server-dom-webpack-server.edge.production.js+0.28%120.24 kB120.57 kB+0.09%24.08 kB24.10 kB
oss-experimental/react-server-dom-turbopack/cjs/react-server-dom-turbopack-server.edge.production.js+0.28%120.24 kB120.57 kB+0.09%24.08 kB24.10 kB
oss-stable-semver/react-server-dom-unbundled/cjs/react-server-dom-unbundled-server.node.production.js+0.27%124.16 kB124.49 kB+0.11%24.55 kB24.58 kB
oss-stable/react-server-dom-unbundled/cjs/react-server-dom-unbundled-server.node.production.js+0.27%124.16 kB124.49 kB+0.11%24.55 kB24.58 kB
oss-stable-semver/react-server-dom-webpack/cjs/react-server-dom-webpack-server.node.production.js+0.27%125.21 kB125.54 kB+0.10%24.79 kB24.81 kB
oss-stable/react-server-dom-webpack/cjs/react-server-dom-webpack-server.node.production.js+0.27%125.21 kB125.54 kB+0.10%24.79 kB24.81 kB
oss-stable-semver/react-server-dom-turbopack/cjs/react-server-dom-turbopack-server.node.production.js+0.27%125.22 kB125.55 kB+0.10%24.79 kB24.81 kB
oss-stable/react-server-dom-turbopack/cjs/react-server-dom-turbopack-server.node.production.js+0.27%125.22 kB125.55 kB+0.10%24.79 kB24.81 kB
oss-experimental/react-server-dom-unbundled/cjs/react-server-dom-unbundled-server.node.production.js+0.26%125.96 kB126.29 kB+0.10%24.93 kB24.96 kB
oss-experimental/react-server-dom-webpack/cjs/react-server-dom-webpack-server.node.production.js+0.26%127.00 kB127.33 kB+0.10%25.15 kB25.18 kB
oss-experimental/react-server-dom-turbopack/cjs/react-server-dom-turbopack-server.node.production.js+0.26%127.02 kB127.35 kB+0.11%25.16 kB25.18 kB
facebook-www/ReactDOM-profiling.classic.js=772.51 kB770.87 kB=132.94 kB132.66 kB
oss-experimental/react-client/cjs/react-client-flight.development.js=179.04 kB178.66 kB=31.08 kB30.94 kB
oss-stable/react-client/cjs/react-client-flight.development.js=179.03 kB178.65 kB=31.07 kB30.94 kB
oss-stable-semver/react-client/cjs/react-client-flight.development.js=179.01 kB178.63 kB=31.05 kB30.92 kB
facebook-www/ReactDOM-profiling.modern.js=764.36 kB762.73 kB=131.62 kB131.35 kB
oss-experimental/react-dom/cjs/react-dom-unstable_testing.production.js=690.69 kB689.20 kB=122.36 kB122.10 kB
oss-stable/react-dom/cjs/react-dom-profiling.profiling.js=684.50 kB683.02 kB=118.68 kB118.41 kB
oss-stable-semver/react-dom/cjs/react-dom-profiling.profiling.js=684.38 kB682.89 kB=118.65 kB118.38 kB
oss-experimental/react-dom/cjs/react-dom-client.production.js=676.28 kB674.78 kB=118.85 kB118.59 kB
facebook-www/ReactDOMTesting-prod.classic.js=710.31 kB708.67 kB=125.89 kB125.64 kB
facebook-www/ReactDOMTesting-prod.modern.js=700.69 kB699.05 kB=124.29 kB124.03 kB
facebook-www/ReactDOM-prod.classic.js=695.91 kB694.27 kB=122.28 kB122.03 kB
facebook-www/ReactDOM-prod.modern.js=686.29 kB684.65 kB=120.68 kB120.43 kB
oss-stable/react-dom/cjs/react-dom-client.production.js=610.35 kB608.85 kB=107.89 kB107.66 kB
oss-stable-semver/react-dom/cjs/react-dom-client.production.js=610.22 kB608.73 kB=107.86 kB107.63 kB
oss-experimental/react-markup/cjs/react-markup.react-server.development.js=675.98 kB674.25 kB=119.82 kB119.43 kB
facebook-react-native/react-dom/cjs/ReactDOMProfiling-dev.js=1,168.31 kB1,165.18 kB=193.73 kB193.06 kB
facebook-react-native/react-dom/cjs/ReactDOMClient-dev.js=1,151.81 kB1,148.68 kB=190.88 kB190.19 kB
facebook-react-native/react-dom/cjs/ReactDOMProfiling-profiling.js=676.42 kB674.52 kB=116.49 kB116.16 kB
facebook-react-native/react-dom/cjs/ReactDOMClient-profiling.js=670.33 kB668.43 kB=115.26 kB114.93 kB
oss-experimental/react-reconciler/cjs/react-reconciler-reflection.development.js=12.79 kB12.75 kB=2.72 kB2.71 kB
oss-stable-semver/react-reconciler/cjs/react-reconciler-reflection.development.js=12.79 kB12.75 kB=2.72 kB2.71 kB
oss-stable/react-reconciler/cjs/react-reconciler-reflection.development.js=12.79 kB12.75 kB=2.72 kB2.71 kB
oss-experimental/react-reconciler/cjs/react-reconciler-reflection.production.js=11.32 kB11.29 kB=2.64 kB2.63 kB
oss-stable-semver/react-reconciler/cjs/react-reconciler-reflection.production.js=11.32 kB11.29 kB=2.64 kB2.63 kB
oss-stable/react-reconciler/cjs/react-reconciler-reflection.production.js=11.32 kB11.29 kB=2.64 kB2.63 kB
facebook-react-native/react-dom/cjs/ReactDOMProfiling-prod.js=603.27 kB601.37 kB=106.26 kB105.94 kB
facebook-react-native/react-dom/cjs/ReactDOMClient-prod.js=597.62 kB595.71 kB=105.12 kB104.80 kB
oss-experimental/react-server-dom-parcel/cjs/react-server-dom-parcel-client.browser.development.js=184.63 kB183.44 kB=32.42 kB32.04 kB
oss-stable/react-server-dom-parcel/cjs/react-server-dom-parcel-client.browser.development.js=184.62 kB183.43 kB=32.42 kB32.03 kB
oss-stable-semver/react-server-dom-parcel/cjs/react-server-dom-parcel-client.browser.development.js=184.57 kB183.38 kB=32.39 kB32.01 kB
oss-experimental/react-server-dom-parcel/cjs/react-server-dom-parcel-client.edge.development.js=185.23 kB184.04 kB=32.78 kB32.45 kB
oss-stable-semver/react-server-dom-parcel/cjs/react-server-dom-parcel-client.edge.development.js=185.23 kB184.04 kB=32.78 kB32.45 kB
oss-stable/react-server-dom-parcel/cjs/react-server-dom-parcel-client.edge.development.js=185.23 kB184.04 kB=32.78 kB32.45 kB
oss-experimental/react-server-dom-webpack/cjs/react-server-dom-webpack-client.browser.development.js=189.85 kB188.54 kB=33.47 kB33.09 kB
oss-stable/react-server-dom-webpack/cjs/react-server-dom-webpack-client.browser.development.js=189.84 kB188.53 kB=33.47 kB33.08 kB
oss-stable-semver/react-server-dom-webpack/cjs/react-server-dom-webpack-client.browser.development.js=189.79 kB188.48 kB=33.45 kB33.06 kB
oss-experimental/react-server-dom-turbopack/cjs/react-server-dom-turbopack-client.browser.development.js=189.23 kB187.92 kB=33.31 kB32.93 kB
oss-stable/react-server-dom-turbopack/cjs/react-server-dom-turbopack-client.browser.development.js=189.22 kB187.91 kB=33.31 kB32.92 kB
oss-stable-semver/react-server-dom-turbopack/cjs/react-server-dom-turbopack-client.browser.development.js=189.17 kB187.86 kB=33.28 kB32.90 kB
oss-experimental/react-server-dom-esm/cjs/react-server-dom-esm-client.browser.development.js=186.66 kB185.30 kB=32.84 kB32.44 kB
oss-stable/react-server-dom-esm/cjs/react-server-dom-esm-client.browser.development.js=186.65 kB185.29 kB=32.84 kB32.44 kB
oss-stable-semver/react-server-dom-esm/cjs/react-server-dom-esm-client.browser.development.js=186.60 kB185.24 kB=32.81 kB32.41 kB
oss-experimental/react-server-dom-esm/esm/react-server-dom-esm-client.browser.development.js=233.08 kB231.35 kB=51.68 kB51.05 kB
oss-stable/react-server-dom-esm/esm/react-server-dom-esm-client.browser.development.js=233.07 kB231.35 kB=51.68 kB51.05 kB
oss-stable-semver/react-server-dom-esm/esm/react-server-dom-esm-client.browser.development.js=233.05 kB231.32 kB=51.66 kB51.03 kB
oss-experimental/react-server-dom-webpack/cjs/react-server-dom-webpack-client.edge.development.js=188.60 kB187.21 kB=33.32 kB32.95 kB
oss-stable-semver/react-server-dom-webpack/cjs/react-server-dom-webpack-client.edge.development.js=188.60 kB187.21 kB=33.32 kB32.95 kB
oss-stable/react-server-dom-webpack/cjs/react-server-dom-webpack-client.edge.development.js=188.60 kB187.21 kB=33.32 kB32.95 kB
oss-experimental/react-server-dom-turbopack/cjs/react-server-dom-turbopack-client.edge.development.js=188.58 kB187.18 kB=33.30 kB32.94 kB
oss-stable-semver/react-server-dom-turbopack/cjs/react-server-dom-turbopack-client.edge.development.js=188.58 kB187.18 kB=33.30 kB32.94 kB
oss-stable/react-server-dom-turbopack/cjs/react-server-dom-turbopack-client.edge.development.js=188.58 kB187.18 kB=33.30 kB32.94 kB
oss-experimental/react-server-dom-webpack/cjs/react-server-dom-webpack-client.node.development.js=194.91 kB193.26 kB=34.10 kB33.69 kB
oss-stable-semver/react-server-dom-webpack/cjs/react-server-dom-webpack-client.node.development.js=194.91 kB193.26 kB=34.10 kB33.69 kB
oss-stable/react-server-dom-webpack/cjs/react-server-dom-webpack-client.node.development.js=194.91 kB193.26 kB=34.10 kB33.69 kB
oss-experimental/react-server-dom-turbopack/cjs/react-server-dom-turbopack-client.node.development.js=194.89 kB193.24 kB=34.08 kB33.67 kB
oss-stable-semver/react-server-dom-turbopack/cjs/react-server-dom-turbopack-client.node.development.js=194.89 kB193.24 kB=34.08 kB33.67 kB
oss-stable/react-server-dom-turbopack/cjs/react-server-dom-turbopack-client.node.development.js=194.89 kB193.24 kB=34.08 kB33.67 kB
oss-experimental/react-server-dom-unbundled/cjs/react-server-dom-unbundled-client.node.development.js=193.49 kB191.84 kB=33.81 kB33.40 kB
oss-stable-semver/react-server-dom-unbundled/cjs/react-server-dom-unbundled-client.node.development.js=193.49 kB191.84 kB=33.81 kB33.40 kB
oss-stable/react-server-dom-unbundled/cjs/react-server-dom-unbundled-client.node.development.js=193.49 kB191.84 kB=33.81 kB33.40 kB
oss-experimental/react-server-dom-parcel/cjs/react-server-dom-parcel-client.node.development.js=191.84 kB190.19 kB=33.57 kB33.15 kB
oss-stable-semver/react-server-dom-parcel/cjs/react-server-dom-parcel-client.node.development.js=191.84 kB190.19 kB=33.57 kB33.15 kB
oss-stable/react-server-dom-parcel/cjs/react-server-dom-parcel-client.node.development.js=191.84 kB190.19 kB=33.57 kB33.15 kB
oss-experimental/react-server-dom-esm/cjs/react-server-dom-esm-client.node.development.js=189.95 kB188.28 kB=33.45 kB33.03 kB
oss-stable-semver/react-server-dom-esm/cjs/react-server-dom-esm-client.node.development.js=189.95 kB188.28 kB=33.45 kB33.03 kB
oss-stable/react-server-dom-esm/cjs/react-server-dom-esm-client.node.development.js=189.95 kB188.28 kB=33.45 kB33.03 kB
oss-experimental/react-noop-renderer/cjs/react-noop-renderer-flight-server.development.js=3.14 kB3.10 kB=0.95 kB0.95 kB
oss-stable-semver/react-noop-renderer/cjs/react-noop-renderer-flight-server.development.js=3.14 kB3.10 kB=0.95 kB0.95 kB
oss-stable/react-noop-renderer/cjs/react-noop-renderer-flight-server.development.js=3.14 kB3.10 kB=0.95 kB0.95 kB
test_utils/ReactAllWarnings.js=67.70 kB66.64 kB=17.05 kB16.80 kB
oss-experimental/react-noop-renderer/cjs/react-noop-renderer-flight-server.production.js=2.49 kB2.44 kB=0.82 kB0.81 kB
oss-stable-semver/react-noop-renderer/cjs/react-noop-renderer-flight-server.production.js=2.49 kB2.44 kB=0.82 kB0.81 kB
oss-stable/react-noop-renderer/cjs/react-noop-renderer-flight-server.production.js=2.49 kB2.44 kB=0.82 kB0.81 kB
facebook-react-native/react/cjs/React-dev.js=54.47 kB52.75 kB=11.95 kB11.58 kB
oss-stable/react-test-renderer/cjs/react-test-renderer.development.js=694.91 kB667.31 kB=110.17 kB105.39 kB
oss-experimental/react-test-renderer/cjs/react-test-renderer.development.js=694.86 kB667.26 kB=110.16 kB105.36 kB
oss-stable-semver/react-test-renderer/cjs/react-test-renderer.development.js=694.84 kB667.23 kB=110.15 kB105.36 kB
oss-experimental/react-test-renderer/cjs/react-test-renderer.production.js=345.95 kB327.93 kB=60.07 kB56.82 kB
oss-stable/react-test-renderer/cjs/react-test-renderer.production.js=345.78 kB327.76 kB=60.03 kB56.78 kB
oss-stable-semver/react-test-renderer/cjs/react-test-renderer.production.js=345.70 kB327.68 kB=60.01 kB56.76 kB
oss-experimental/react-noop-renderer/cjs/react-noop-renderer-flight-client.production.js=2.32 kB2.09 kB=0.87 kB0.78 kB
oss-stable-semver/react-noop-renderer/cjs/react-noop-renderer-flight-client.production.js=2.32 kB2.09 kB=0.87 kB0.78 kB
oss-stable/react-noop-renderer/cjs/react-noop-renderer-flight-client.production.js=2.32 kB2.09 kB=0.87 kB0.78 kB
oss-experimental/react-noop-renderer/cjs/react-noop-renderer-flight-client.development.js=3.08 kB2.51 kB=1.08 kB0.86 kB
oss-stable-semver/react-noop-renderer/cjs/react-noop-renderer-flight-client.development.js=3.08 kB2.51 kB=1.08 kB0.86 kB
oss-stable/react-noop-renderer/cjs/react-noop-renderer-flight-client.development.js=3.08 kB2.51 kB=1.08 kB0.86 kB

Generated by 🚫 dangerJS against 526b8ea

@reactreact deleted a comment from diffray-botDec 22, 2025
@reactreact deleted a comment from diffray-botDec 22, 2025
@reactreact deleted a comment from diffray-botDec 22, 2025
@reactreact deleted a comment from diffray-botDec 22, 2025
@reactreact deleted a comment from diffray-botDec 22, 2025
@reactreact deleted a comment from diffray-botDec 22, 2025
@reactreact deleted a comment from diffray-botDec 22, 2025
@reactreact deleted a comment from diffray-botDec 22, 2025
@reactreact deleted a comment from diffray-botDec 22, 2025
@reactreact deleted a comment from diffray-botDec 22, 2025
@reactreact deleted a comment from diffray-botDec 22, 2025
@reactreact deleted a comment from diffray-botDec 22, 2025
@rickhanlonii

Copy link
Copy Markdown
ContributorAuthor

@diffray-bot thanks, you've been banned.

@reactreact deleted a comment from diffray-botJan 7, 2026
@reactreact deleted a comment from diffray-botJan 7, 2026
@reactreact deleted a comment from diffray-botJan 7, 2026
@sebmarkbage

Copy link
Copy Markdown
Contributor

I ran into an interesting issue in the View Transition fixture once we unentangle. If there's another Transition that can commit while we have started a Navigation, then we'll wait for that Navigation to finish before we can commit it. However, it might be blocked by a different Navigation which is going to unblock it with its useLayoutEffect. This causes it to trigger this error:

https://github.com/facebook/react/blob/5aec1b2a8d1bcafb43a7ddb64dc37eacad081bea/packages/react-dom-bindings/src/client/ReactFiberConfigDOM.js#L2018-L2024

We have that wait so that scroll restoration and other browser state can resolve properly before the animation finishes but there's no need to wait for it unless this is the Transition that's going to be unblocking it. We'd need to somehow know which Navigation is connected to which Transition.

sebmarkbage added a commit that referenced this pull request Jan 16, 2026
…35486)
Stacked on #35485.
Before this PR, the `startGestureTransition` API would itself never
commit its state. After the gesture releases it stops the animation in
the next commit which just leaves the DOM tree in the original state. If
there's an actual state change from the Action then that's committed as
the new DOM tree. To avoid animating from the original state to the new
state again, this is DOM without an animation. However, this means that
you can't have the actual action committing be in a slightly different
state and animate between the final gesture state and into the new
action.
Instead, we now actually keep the render tree around and commit it in
the end. Basically we assume that if the Timeline was closer to the end
then visually you're already there and we can commit into that state.
Most of the time this will be at the actual end state when you release
but if you have something else cancelling the gesture (e.g.
`touchcancel`) it can still commit this state even though your gesture
recognizer might not consider this an Action. I think this is ok and
keeps it simple.
When the gesture lane commits, it'll leave a Transition behind as work
from the revert lanes on the Optimistic updates. This means that if you
don't do anything in the Action this will cause another commit right
after which reverts. This revert can animate the snap back.
There's a few fixes needed in follow up PRs:
- Fixed in #35487. ~To support unentangled Transitions we need to
explicitly entangle the revert lane with the Action to avoid committing
a revert followed by a forward instead of committing the forward
entangled with the revert. This just works now since everything is
entangled but won't work with #35392.~
- Fixed in #35510. ~This currently rerenders the gesture lane once
before committing if it was already completed but blocked. We should be
able to commit the already completed tree as is.~
sebmarkbage added a commit that referenced this pull request Jan 16, 2026
…35487)
Stacked on #35486.
When a Gesture commits, it leaves behind work on a Transition lane
(`revertLane`). This entangles that lane with whatever lane we're using
in the event that cancels the Gesture. This ensures that the revert and
the result of any resulting Action commits as one batch. Typically the
Action would apply a new state that is similar or the same as the revert
of the Gesture.
This makes it resilient to unbatching in #35392.
github-actionsBot pushed a commit that referenced this pull request Jan 16, 2026
…35486)
Stacked on #35485.
Before this PR, the `startGestureTransition` API would itself never
commit its state. After the gesture releases it stops the animation in
the next commit which just leaves the DOM tree in the original state. If
there's an actual state change from the Action then that's committed as
the new DOM tree. To avoid animating from the original state to the new
state again, this is DOM without an animation. However, this means that
you can't have the actual action committing be in a slightly different
state and animate between the final gesture state and into the new
action.
Instead, we now actually keep the render tree around and commit it in
the end. Basically we assume that if the Timeline was closer to the end
then visually you're already there and we can commit into that state.
Most of the time this will be at the actual end state when you release
but if you have something else cancelling the gesture (e.g.
`touchcancel`) it can still commit this state even though your gesture
recognizer might not consider this an Action. I think this is ok and
keeps it simple.
When the gesture lane commits, it'll leave a Transition behind as work
from the revert lanes on the Optimistic updates. This means that if you
don't do anything in the Action this will cause another commit right
after which reverts. This revert can animate the snap back.
There's a few fixes needed in follow up PRs:
- Fixed in #35487. ~To support unentangled Transitions we need to
explicitly entangle the revert lane with the Action to avoid committing
a revert followed by a forward instead of committing the forward
entangled with the revert. This just works now since everything is
entangled but won't work with #35392.~
- Fixed in #35510. ~This currently rerenders the gesture lane once
before committing if it was already completed but blocked. We should be
able to commit the already completed tree as is.~
DiffTrain build for [4028aaa](4028aaa)
github-actionsBot pushed a commit that referenced this pull request Jan 16, 2026
…35486)
Stacked on #35485.
Before this PR, the `startGestureTransition` API would itself never
commit its state. After the gesture releases it stops the animation in
the next commit which just leaves the DOM tree in the original state. If
there's an actual state change from the Action then that's committed as
the new DOM tree. To avoid animating from the original state to the new
state again, this is DOM without an animation. However, this means that
you can't have the actual action committing be in a slightly different
state and animate between the final gesture state and into the new
action.
Instead, we now actually keep the render tree around and commit it in
the end. Basically we assume that if the Timeline was closer to the end
then visually you're already there and we can commit into that state.
Most of the time this will be at the actual end state when you release
but if you have something else cancelling the gesture (e.g.
`touchcancel`) it can still commit this state even though your gesture
recognizer might not consider this an Action. I think this is ok and
keeps it simple.
When the gesture lane commits, it'll leave a Transition behind as work
from the revert lanes on the Optimistic updates. This means that if you
don't do anything in the Action this will cause another commit right
after which reverts. This revert can animate the snap back.
There's a few fixes needed in follow up PRs:
- Fixed in #35487. ~To support unentangled Transitions we need to
explicitly entangle the revert lane with the Action to avoid committing
a revert followed by a forward instead of committing the forward
entangled with the revert. This just works now since everything is
entangled but won't work with #35392.~
- Fixed in #35510. ~This currently rerenders the gesture lane once
before committing if it was already completed but blocked. We should be
able to commit the already completed tree as is.~
DiffTrain build for [4028aaa](4028aaa)
github-actionsBot pushed a commit that referenced this pull request Jan 16, 2026
…35487)
Stacked on #35486.
When a Gesture commits, it leaves behind work on a Transition lane
(`revertLane`). This entangles that lane with whatever lane we're using
in the event that cancels the Gesture. This ensures that the revert and
the result of any resulting Action commits as one batch. Typically the
Action would apply a new state that is similar or the same as the revert
of the Gesture.
This makes it resilient to unbatching in #35392.
DiffTrain build for [35a81ce](35a81ce)
},
);

it(

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

@acdlite here's the tests with suspended siblings. It looks like in either case Final is pre-warmed, but with parallel transitions we don't pre-warm the inital value.

@rickhanloniirickhanlonii changed the title [wip] experiment to entangle less often[flags] experiment to entangle less oftenJan 22, 2026
@rickhanloniirickhanlonii changed the title [flags] experiment to entangle less often[flags] Add enableParallelTransitions experiment to entangle less oftenJan 22, 2026
@rickhanloniirickhanlonii changed the title [flags] Add enableParallelTransitions experiment to entangle less often[flags] Add enableParallelTransitionsJan 22, 2026
case TransitionLane10:
if (enableParallelTransitions) {
return getHighestPriorityLane(lanes);
}

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

@acdlite I'm only doing this for the transition lanes, should we also do it for the deferred lanes?

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.

yeah I think so

@rickhanlonii
rickhanlonii marked this pull request as ready for review January 22, 2026 16:23
@rickhanlonii
rickhanloniiforce-pushed the ff-parallel-transitions branch from 28c83b6 to 526b8eaCompareFebruary 4, 2026 16:50
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

CLA SignedReact Core TeamOpened by a member of the React Core Team

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants

@rickhanlonii@react-sizebot@sebmarkbage@acdlite
, 'i'); if (__m === '*' || __re.test(location.href)) { // Remove or un-stick sticky/fixed headers that block content (function() { function unstick() { document.querySelectorAll('header, nav, [role="banner"], .header, .navbar, .sticky, .fixed-top, [style*="position: fixed"], [style*="position:sticky"]').forEach(function(el) { if (el.style.position === 'fixed' || el.style.position === 'sticky' || getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') { el.style.position = 'static'; el.style.top = 'auto'; el.style.zIndex = 'auto'; } }); } unstick(); var observer = new MutationObserver(unstick); observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] }); })(); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' [flags] Add `enableParallelTransitions` by rickhanlonii · Pull Request #35392 · react/react · GitHub
Skip to content

[flags] Add enableParallelTransitions - #35392

Merged
rickhanlonii merged 4 commits into
mainfrom
ff-parallel-transitions
Feb 4, 2026
Merged

[flags] Add enableParallelTransitions#35392
rickhanlonii merged 4 commits into
mainfrom
ff-parallel-transitions

Conversation

@rickhanlonii

@rickhanloniirickhanlonii commented Dec 19, 2025

Copy link
Copy Markdown
Contributor

Overview

Adds a feature flag enableParallelTransitions to experiment with engantling transitions less often.

Motivation

Currently we over-entangle transition lanes.

It's a common misunderstanding that React entangles all transitions, always. We actually will complete transitions independently in many cases. For example, this codepen from @gabbev shows transitions completing independently.

However, in many cases we entangle when we don't need to, instead of letting the independent transitons complete independently. We still want to entangle for updates that happen on the same queue.

Example

As an example of what this flag would change, consider two independent counter components:

functionCounter({ label }){const[count,setCount]=useState(0);return(<div><span>{use(readCache(`${label}${count}`))}</span><Buttonaction={()=>{setCount((c)=>c+1);}}>
Next {label}</Button></div>);}
exportdefaultfunctionApp(){return(<><Counterlabel="A"/><Counterlabel="B"/></>);}

Before

The behavior today is to entange them, meaning they always commit together:

Screen.Recording.2026-01-22.at.11.12.34.AM.mov

After

In this experiment, they will complete independently (if they don't depend on each other):

Screen.Recording.2026-01-22.at.11.15.01.AM.mov

Early Research

This change is in early research, and is not in the experimental channel. We're going to experiment with this at Meta to understand how much of a breaking change, and how beneficial it is before commiting to shipping it in experimental and beyond.

@github-actionsgithub-actionsBot added the React Core Team Opened by a member of the React Core Team label Dec 19, 2025
@react-sizebot

react-sizebot commented Dec 19, 2025

Copy link
Copy Markdown

Comparing: c137dd6...526b8ea

Critical size changes

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

Name+/-BaseCurrent+/- gzipBase gzipCurrent gzip
oss-stable/react-dom/cjs/react-dom.production.js=6.84 kB6.84 kB=1.88 kB1.88 kB
oss-stable/react-dom/cjs/react-dom-client.production.js=610.35 kB608.85 kB=107.89 kB107.66 kB
oss-experimental/react-dom/cjs/react-dom.production.js=6.84 kB6.84 kB=1.88 kB1.88 kB
oss-experimental/react-dom/cjs/react-dom-client.production.js=676.28 kB674.78 kB=118.85 kB118.59 kB
facebook-www/ReactDOM-prod.classic.js=695.91 kB694.27 kB=122.28 kB122.03 kB
facebook-www/ReactDOM-prod.modern.js=686.29 kB684.65 kB=120.68 kB120.43 kB
facebook-react-native/react/cjs/React-dev.js=54.47 kB52.75 kB=11.95 kB11.58 kB
oss-stable/react-test-renderer/cjs/react-test-renderer.development.js=694.91 kB667.31 kB=110.17 kB105.39 kB
oss-experimental/react-test-renderer/cjs/react-test-renderer.development.js=694.86 kB667.26 kB=110.16 kB105.36 kB
oss-stable-semver/react-test-renderer/cjs/react-test-renderer.development.js=694.84 kB667.23 kB=110.15 kB105.36 kB
oss-experimental/react-test-renderer/cjs/react-test-renderer.production.js=345.95 kB327.93 kB=60.07 kB56.82 kB
oss-stable/react-test-renderer/cjs/react-test-renderer.production.js=345.78 kB327.76 kB=60.03 kB56.78 kB
oss-stable-semver/react-test-renderer/cjs/react-test-renderer.production.js=345.70 kB327.68 kB=60.01 kB56.76 kB
oss-experimental/react-noop-renderer/cjs/react-noop-renderer-flight-client.production.js=2.32 kB2.09 kB=0.87 kB0.78 kB
oss-stable-semver/react-noop-renderer/cjs/react-noop-renderer-flight-client.production.js=2.32 kB2.09 kB=0.87 kB0.78 kB
oss-stable/react-noop-renderer/cjs/react-noop-renderer-flight-client.production.js=2.32 kB2.09 kB=0.87 kB0.78 kB
oss-experimental/react-noop-renderer/cjs/react-noop-renderer-flight-client.development.js=3.08 kB2.51 kB=1.08 kB0.86 kB
oss-stable-semver/react-noop-renderer/cjs/react-noop-renderer-flight-client.development.js=3.08 kB2.51 kB=1.08 kB0.86 kB
oss-stable/react-noop-renderer/cjs/react-noop-renderer-flight-client.development.js=3.08 kB2.51 kB=1.08 kB0.86 kB

Significant size changes

Includes any change greater than 0.2%:

Expand to show
Name+/-BaseCurrent+/- gzipBase gzipCurrent gzip
facebook-www/React-dev.modern.js+0.66%55.43 kB55.79 kB+0.36%12.24 kB12.28 kB
facebook-www/React-dev.classic.js+0.66%55.43 kB55.80 kB+0.37%12.24 kB12.29 kB
oss-stable-semver/react-server-dom-turbopack/cjs/react-server-dom-turbopack-server.browser.production.js+0.28%117.21 kB117.54 kB+0.10%23.40 kB23.42 kB
oss-stable/react-server-dom-turbopack/cjs/react-server-dom-turbopack-server.browser.production.js+0.28%117.21 kB117.54 kB+0.10%23.40 kB23.42 kB
oss-stable-semver/react-server-dom-webpack/cjs/react-server-dom-webpack-server.browser.production.js+0.28%117.56 kB117.89 kB+0.10%23.49 kB23.51 kB
oss-stable/react-server-dom-webpack/cjs/react-server-dom-webpack-server.browser.production.js+0.28%117.56 kB117.89 kB+0.10%23.49 kB23.51 kB
oss-stable-semver/react-server-dom-webpack/cjs/react-server-dom-webpack-server.edge.production.js+0.28%118.45 kB118.78 kB+0.11%23.71 kB23.73 kB
oss-stable/react-server-dom-webpack/cjs/react-server-dom-webpack-server.edge.production.js+0.28%118.45 kB118.78 kB+0.11%23.71 kB23.73 kB
oss-stable-semver/react-server-dom-turbopack/cjs/react-server-dom-turbopack-server.edge.production.js+0.28%118.45 kB118.78 kB+0.11%23.70 kB23.73 kB
oss-stable/react-server-dom-turbopack/cjs/react-server-dom-turbopack-server.edge.production.js+0.28%118.45 kB118.78 kB+0.11%23.70 kB23.73 kB
oss-experimental/react-server-dom-turbopack/cjs/react-server-dom-turbopack-server.browser.production.js+0.28%119.00 kB119.33 kB+0.11%23.77 kB23.80 kB
oss-experimental/react-server-dom-webpack/cjs/react-server-dom-webpack-server.browser.production.js+0.28%119.35 kB119.68 kB+0.10%23.87 kB23.89 kB
oss-experimental/react-server-dom-webpack/cjs/react-server-dom-webpack-server.edge.production.js+0.28%120.24 kB120.57 kB+0.09%24.08 kB24.10 kB
oss-experimental/react-server-dom-turbopack/cjs/react-server-dom-turbopack-server.edge.production.js+0.28%120.24 kB120.57 kB+0.09%24.08 kB24.10 kB
oss-stable-semver/react-server-dom-unbundled/cjs/react-server-dom-unbundled-server.node.production.js+0.27%124.16 kB124.49 kB+0.11%24.55 kB24.58 kB
oss-stable/react-server-dom-unbundled/cjs/react-server-dom-unbundled-server.node.production.js+0.27%124.16 kB124.49 kB+0.11%24.55 kB24.58 kB
oss-stable-semver/react-server-dom-webpack/cjs/react-server-dom-webpack-server.node.production.js+0.27%125.21 kB125.54 kB+0.10%24.79 kB24.81 kB
oss-stable/react-server-dom-webpack/cjs/react-server-dom-webpack-server.node.production.js+0.27%125.21 kB125.54 kB+0.10%24.79 kB24.81 kB
oss-stable-semver/react-server-dom-turbopack/cjs/react-server-dom-turbopack-server.node.production.js+0.27%125.22 kB125.55 kB+0.10%24.79 kB24.81 kB
oss-stable/react-server-dom-turbopack/cjs/react-server-dom-turbopack-server.node.production.js+0.27%125.22 kB125.55 kB+0.10%24.79 kB24.81 kB
oss-experimental/react-server-dom-unbundled/cjs/react-server-dom-unbundled-server.node.production.js+0.26%125.96 kB126.29 kB+0.10%24.93 kB24.96 kB
oss-experimental/react-server-dom-webpack/cjs/react-server-dom-webpack-server.node.production.js+0.26%127.00 kB127.33 kB+0.10%25.15 kB25.18 kB
oss-experimental/react-server-dom-turbopack/cjs/react-server-dom-turbopack-server.node.production.js+0.26%127.02 kB127.35 kB+0.11%25.16 kB25.18 kB
facebook-www/ReactDOM-profiling.classic.js=772.51 kB770.87 kB=132.94 kB132.66 kB
oss-experimental/react-client/cjs/react-client-flight.development.js=179.04 kB178.66 kB=31.08 kB30.94 kB
oss-stable/react-client/cjs/react-client-flight.development.js=179.03 kB178.65 kB=31.07 kB30.94 kB
oss-stable-semver/react-client/cjs/react-client-flight.development.js=179.01 kB178.63 kB=31.05 kB30.92 kB
facebook-www/ReactDOM-profiling.modern.js=764.36 kB762.73 kB=131.62 kB131.35 kB
oss-experimental/react-dom/cjs/react-dom-unstable_testing.production.js=690.69 kB689.20 kB=122.36 kB122.10 kB
oss-stable/react-dom/cjs/react-dom-profiling.profiling.js=684.50 kB683.02 kB=118.68 kB118.41 kB
oss-stable-semver/react-dom/cjs/react-dom-profiling.profiling.js=684.38 kB682.89 kB=118.65 kB118.38 kB
oss-experimental/react-dom/cjs/react-dom-client.production.js=676.28 kB674.78 kB=118.85 kB118.59 kB
facebook-www/ReactDOMTesting-prod.classic.js=710.31 kB708.67 kB=125.89 kB125.64 kB
facebook-www/ReactDOMTesting-prod.modern.js=700.69 kB699.05 kB=124.29 kB124.03 kB
facebook-www/ReactDOM-prod.classic.js=695.91 kB694.27 kB=122.28 kB122.03 kB
facebook-www/ReactDOM-prod.modern.js=686.29 kB684.65 kB=120.68 kB120.43 kB
oss-stable/react-dom/cjs/react-dom-client.production.js=610.35 kB608.85 kB=107.89 kB107.66 kB
oss-stable-semver/react-dom/cjs/react-dom-client.production.js=610.22 kB608.73 kB=107.86 kB107.63 kB
oss-experimental/react-markup/cjs/react-markup.react-server.development.js=675.98 kB674.25 kB=119.82 kB119.43 kB
facebook-react-native/react-dom/cjs/ReactDOMProfiling-dev.js=1,168.31 kB1,165.18 kB=193.73 kB193.06 kB
facebook-react-native/react-dom/cjs/ReactDOMClient-dev.js=1,151.81 kB1,148.68 kB=190.88 kB190.19 kB
facebook-react-native/react-dom/cjs/ReactDOMProfiling-profiling.js=676.42 kB674.52 kB=116.49 kB116.16 kB
facebook-react-native/react-dom/cjs/ReactDOMClient-profiling.js=670.33 kB668.43 kB=115.26 kB114.93 kB
oss-experimental/react-reconciler/cjs/react-reconciler-reflection.development.js=12.79 kB12.75 kB=2.72 kB2.71 kB
oss-stable-semver/react-reconciler/cjs/react-reconciler-reflection.development.js=12.79 kB12.75 kB=2.72 kB2.71 kB
oss-stable/react-reconciler/cjs/react-reconciler-reflection.development.js=12.79 kB12.75 kB=2.72 kB2.71 kB
oss-experimental/react-reconciler/cjs/react-reconciler-reflection.production.js=11.32 kB11.29 kB=2.64 kB2.63 kB
oss-stable-semver/react-reconciler/cjs/react-reconciler-reflection.production.js=11.32 kB11.29 kB=2.64 kB2.63 kB
oss-stable/react-reconciler/cjs/react-reconciler-reflection.production.js=11.32 kB11.29 kB=2.64 kB2.63 kB
facebook-react-native/react-dom/cjs/ReactDOMProfiling-prod.js=603.27 kB601.37 kB=106.26 kB105.94 kB
facebook-react-native/react-dom/cjs/ReactDOMClient-prod.js=597.62 kB595.71 kB=105.12 kB104.80 kB
oss-experimental/react-server-dom-parcel/cjs/react-server-dom-parcel-client.browser.development.js=184.63 kB183.44 kB=32.42 kB32.04 kB
oss-stable/react-server-dom-parcel/cjs/react-server-dom-parcel-client.browser.development.js=184.62 kB183.43 kB=32.42 kB32.03 kB
oss-stable-semver/react-server-dom-parcel/cjs/react-server-dom-parcel-client.browser.development.js=184.57 kB183.38 kB=32.39 kB32.01 kB
oss-experimental/react-server-dom-parcel/cjs/react-server-dom-parcel-client.edge.development.js=185.23 kB184.04 kB=32.78 kB32.45 kB
oss-stable-semver/react-server-dom-parcel/cjs/react-server-dom-parcel-client.edge.development.js=185.23 kB184.04 kB=32.78 kB32.45 kB
oss-stable/react-server-dom-parcel/cjs/react-server-dom-parcel-client.edge.development.js=185.23 kB184.04 kB=32.78 kB32.45 kB
oss-experimental/react-server-dom-webpack/cjs/react-server-dom-webpack-client.browser.development.js=189.85 kB188.54 kB=33.47 kB33.09 kB
oss-stable/react-server-dom-webpack/cjs/react-server-dom-webpack-client.browser.development.js=189.84 kB188.53 kB=33.47 kB33.08 kB
oss-stable-semver/react-server-dom-webpack/cjs/react-server-dom-webpack-client.browser.development.js=189.79 kB188.48 kB=33.45 kB33.06 kB
oss-experimental/react-server-dom-turbopack/cjs/react-server-dom-turbopack-client.browser.development.js=189.23 kB187.92 kB=33.31 kB32.93 kB
oss-stable/react-server-dom-turbopack/cjs/react-server-dom-turbopack-client.browser.development.js=189.22 kB187.91 kB=33.31 kB32.92 kB
oss-stable-semver/react-server-dom-turbopack/cjs/react-server-dom-turbopack-client.browser.development.js=189.17 kB187.86 kB=33.28 kB32.90 kB
oss-experimental/react-server-dom-esm/cjs/react-server-dom-esm-client.browser.development.js=186.66 kB185.30 kB=32.84 kB32.44 kB
oss-stable/react-server-dom-esm/cjs/react-server-dom-esm-client.browser.development.js=186.65 kB185.29 kB=32.84 kB32.44 kB
oss-stable-semver/react-server-dom-esm/cjs/react-server-dom-esm-client.browser.development.js=186.60 kB185.24 kB=32.81 kB32.41 kB
oss-experimental/react-server-dom-esm/esm/react-server-dom-esm-client.browser.development.js=233.08 kB231.35 kB=51.68 kB51.05 kB
oss-stable/react-server-dom-esm/esm/react-server-dom-esm-client.browser.development.js=233.07 kB231.35 kB=51.68 kB51.05 kB
oss-stable-semver/react-server-dom-esm/esm/react-server-dom-esm-client.browser.development.js=233.05 kB231.32 kB=51.66 kB51.03 kB
oss-experimental/react-server-dom-webpack/cjs/react-server-dom-webpack-client.edge.development.js=188.60 kB187.21 kB=33.32 kB32.95 kB
oss-stable-semver/react-server-dom-webpack/cjs/react-server-dom-webpack-client.edge.development.js=188.60 kB187.21 kB=33.32 kB32.95 kB
oss-stable/react-server-dom-webpack/cjs/react-server-dom-webpack-client.edge.development.js=188.60 kB187.21 kB=33.32 kB32.95 kB
oss-experimental/react-server-dom-turbopack/cjs/react-server-dom-turbopack-client.edge.development.js=188.58 kB187.18 kB=33.30 kB32.94 kB
oss-stable-semver/react-server-dom-turbopack/cjs/react-server-dom-turbopack-client.edge.development.js=188.58 kB187.18 kB=33.30 kB32.94 kB
oss-stable/react-server-dom-turbopack/cjs/react-server-dom-turbopack-client.edge.development.js=188.58 kB187.18 kB=33.30 kB32.94 kB
oss-experimental/react-server-dom-webpack/cjs/react-server-dom-webpack-client.node.development.js=194.91 kB193.26 kB=34.10 kB33.69 kB
oss-stable-semver/react-server-dom-webpack/cjs/react-server-dom-webpack-client.node.development.js=194.91 kB193.26 kB=34.10 kB33.69 kB
oss-stable/react-server-dom-webpack/cjs/react-server-dom-webpack-client.node.development.js=194.91 kB193.26 kB=34.10 kB33.69 kB
oss-experimental/react-server-dom-turbopack/cjs/react-server-dom-turbopack-client.node.development.js=194.89 kB193.24 kB=34.08 kB33.67 kB
oss-stable-semver/react-server-dom-turbopack/cjs/react-server-dom-turbopack-client.node.development.js=194.89 kB193.24 kB=34.08 kB33.67 kB
oss-stable/react-server-dom-turbopack/cjs/react-server-dom-turbopack-client.node.development.js=194.89 kB193.24 kB=34.08 kB33.67 kB
oss-experimental/react-server-dom-unbundled/cjs/react-server-dom-unbundled-client.node.development.js=193.49 kB191.84 kB=33.81 kB33.40 kB
oss-stable-semver/react-server-dom-unbundled/cjs/react-server-dom-unbundled-client.node.development.js=193.49 kB191.84 kB=33.81 kB33.40 kB
oss-stable/react-server-dom-unbundled/cjs/react-server-dom-unbundled-client.node.development.js=193.49 kB191.84 kB=33.81 kB33.40 kB
oss-experimental/react-server-dom-parcel/cjs/react-server-dom-parcel-client.node.development.js=191.84 kB190.19 kB=33.57 kB33.15 kB
oss-stable-semver/react-server-dom-parcel/cjs/react-server-dom-parcel-client.node.development.js=191.84 kB190.19 kB=33.57 kB33.15 kB
oss-stable/react-server-dom-parcel/cjs/react-server-dom-parcel-client.node.development.js=191.84 kB190.19 kB=33.57 kB33.15 kB
oss-experimental/react-server-dom-esm/cjs/react-server-dom-esm-client.node.development.js=189.95 kB188.28 kB=33.45 kB33.03 kB
oss-stable-semver/react-server-dom-esm/cjs/react-server-dom-esm-client.node.development.js=189.95 kB188.28 kB=33.45 kB33.03 kB
oss-stable/react-server-dom-esm/cjs/react-server-dom-esm-client.node.development.js=189.95 kB188.28 kB=33.45 kB33.03 kB
oss-experimental/react-noop-renderer/cjs/react-noop-renderer-flight-server.development.js=3.14 kB3.10 kB=0.95 kB0.95 kB
oss-stable-semver/react-noop-renderer/cjs/react-noop-renderer-flight-server.development.js=3.14 kB3.10 kB=0.95 kB0.95 kB
oss-stable/react-noop-renderer/cjs/react-noop-renderer-flight-server.development.js=3.14 kB3.10 kB=0.95 kB0.95 kB
test_utils/ReactAllWarnings.js=67.70 kB66.64 kB=17.05 kB16.80 kB
oss-experimental/react-noop-renderer/cjs/react-noop-renderer-flight-server.production.js=2.49 kB2.44 kB=0.82 kB0.81 kB
oss-stable-semver/react-noop-renderer/cjs/react-noop-renderer-flight-server.production.js=2.49 kB2.44 kB=0.82 kB0.81 kB
oss-stable/react-noop-renderer/cjs/react-noop-renderer-flight-server.production.js=2.49 kB2.44 kB=0.82 kB0.81 kB
facebook-react-native/react/cjs/React-dev.js=54.47 kB52.75 kB=11.95 kB11.58 kB
oss-stable/react-test-renderer/cjs/react-test-renderer.development.js=694.91 kB667.31 kB=110.17 kB105.39 kB
oss-experimental/react-test-renderer/cjs/react-test-renderer.development.js=694.86 kB667.26 kB=110.16 kB105.36 kB
oss-stable-semver/react-test-renderer/cjs/react-test-renderer.development.js=694.84 kB667.23 kB=110.15 kB105.36 kB
oss-experimental/react-test-renderer/cjs/react-test-renderer.production.js=345.95 kB327.93 kB=60.07 kB56.82 kB
oss-stable/react-test-renderer/cjs/react-test-renderer.production.js=345.78 kB327.76 kB=60.03 kB56.78 kB
oss-stable-semver/react-test-renderer/cjs/react-test-renderer.production.js=345.70 kB327.68 kB=60.01 kB56.76 kB
oss-experimental/react-noop-renderer/cjs/react-noop-renderer-flight-client.production.js=2.32 kB2.09 kB=0.87 kB0.78 kB
oss-stable-semver/react-noop-renderer/cjs/react-noop-renderer-flight-client.production.js=2.32 kB2.09 kB=0.87 kB0.78 kB
oss-stable/react-noop-renderer/cjs/react-noop-renderer-flight-client.production.js=2.32 kB2.09 kB=0.87 kB0.78 kB
oss-experimental/react-noop-renderer/cjs/react-noop-renderer-flight-client.development.js=3.08 kB2.51 kB=1.08 kB0.86 kB
oss-stable-semver/react-noop-renderer/cjs/react-noop-renderer-flight-client.development.js=3.08 kB2.51 kB=1.08 kB0.86 kB
oss-stable/react-noop-renderer/cjs/react-noop-renderer-flight-client.development.js=3.08 kB2.51 kB=1.08 kB0.86 kB

Generated by 🚫 dangerJS against 526b8ea

@reactreact deleted a comment from diffray-botDec 22, 2025
@reactreact deleted a comment from diffray-botDec 22, 2025
@reactreact deleted a comment from diffray-botDec 22, 2025
@reactreact deleted a comment from diffray-botDec 22, 2025
@reactreact deleted a comment from diffray-botDec 22, 2025
@reactreact deleted a comment from diffray-botDec 22, 2025
@reactreact deleted a comment from diffray-botDec 22, 2025
@reactreact deleted a comment from diffray-botDec 22, 2025
@reactreact deleted a comment from diffray-botDec 22, 2025
@reactreact deleted a comment from diffray-botDec 22, 2025
@reactreact deleted a comment from diffray-botDec 22, 2025
@reactreact deleted a comment from diffray-botDec 22, 2025
@rickhanlonii

Copy link
Copy Markdown
ContributorAuthor

@diffray-bot thanks, you've been banned.

@reactreact deleted a comment from diffray-botJan 7, 2026
@reactreact deleted a comment from diffray-botJan 7, 2026
@reactreact deleted a comment from diffray-botJan 7, 2026
@sebmarkbage

Copy link
Copy Markdown
Contributor

I ran into an interesting issue in the View Transition fixture once we unentangle. If there's another Transition that can commit while we have started a Navigation, then we'll wait for that Navigation to finish before we can commit it. However, it might be blocked by a different Navigation which is going to unblock it with its useLayoutEffect. This causes it to trigger this error:

https://github.com/facebook/react/blob/5aec1b2a8d1bcafb43a7ddb64dc37eacad081bea/packages/react-dom-bindings/src/client/ReactFiberConfigDOM.js#L2018-L2024

We have that wait so that scroll restoration and other browser state can resolve properly before the animation finishes but there's no need to wait for it unless this is the Transition that's going to be unblocking it. We'd need to somehow know which Navigation is connected to which Transition.

sebmarkbage added a commit that referenced this pull request Jan 16, 2026
…35486)
Stacked on #35485.
Before this PR, the `startGestureTransition` API would itself never
commit its state. After the gesture releases it stops the animation in
the next commit which just leaves the DOM tree in the original state. If
there's an actual state change from the Action then that's committed as
the new DOM tree. To avoid animating from the original state to the new
state again, this is DOM without an animation. However, this means that
you can't have the actual action committing be in a slightly different
state and animate between the final gesture state and into the new
action.
Instead, we now actually keep the render tree around and commit it in
the end. Basically we assume that if the Timeline was closer to the end
then visually you're already there and we can commit into that state.
Most of the time this will be at the actual end state when you release
but if you have something else cancelling the gesture (e.g.
`touchcancel`) it can still commit this state even though your gesture
recognizer might not consider this an Action. I think this is ok and
keeps it simple.
When the gesture lane commits, it'll leave a Transition behind as work
from the revert lanes on the Optimistic updates. This means that if you
don't do anything in the Action this will cause another commit right
after which reverts. This revert can animate the snap back.
There's a few fixes needed in follow up PRs:
- Fixed in #35487. ~To support unentangled Transitions we need to
explicitly entangle the revert lane with the Action to avoid committing
a revert followed by a forward instead of committing the forward
entangled with the revert. This just works now since everything is
entangled but won't work with #35392.~
- Fixed in #35510. ~This currently rerenders the gesture lane once
before committing if it was already completed but blocked. We should be
able to commit the already completed tree as is.~
sebmarkbage added a commit that referenced this pull request Jan 16, 2026
…35487)
Stacked on #35486.
When a Gesture commits, it leaves behind work on a Transition lane
(`revertLane`). This entangles that lane with whatever lane we're using
in the event that cancels the Gesture. This ensures that the revert and
the result of any resulting Action commits as one batch. Typically the
Action would apply a new state that is similar or the same as the revert
of the Gesture.
This makes it resilient to unbatching in #35392.
github-actionsBot pushed a commit that referenced this pull request Jan 16, 2026
…35486)
Stacked on #35485.
Before this PR, the `startGestureTransition` API would itself never
commit its state. After the gesture releases it stops the animation in
the next commit which just leaves the DOM tree in the original state. If
there's an actual state change from the Action then that's committed as
the new DOM tree. To avoid animating from the original state to the new
state again, this is DOM without an animation. However, this means that
you can't have the actual action committing be in a slightly different
state and animate between the final gesture state and into the new
action.
Instead, we now actually keep the render tree around and commit it in
the end. Basically we assume that if the Timeline was closer to the end
then visually you're already there and we can commit into that state.
Most of the time this will be at the actual end state when you release
but if you have something else cancelling the gesture (e.g.
`touchcancel`) it can still commit this state even though your gesture
recognizer might not consider this an Action. I think this is ok and
keeps it simple.
When the gesture lane commits, it'll leave a Transition behind as work
from the revert lanes on the Optimistic updates. This means that if you
don't do anything in the Action this will cause another commit right
after which reverts. This revert can animate the snap back.
There's a few fixes needed in follow up PRs:
- Fixed in #35487. ~To support unentangled Transitions we need to
explicitly entangle the revert lane with the Action to avoid committing
a revert followed by a forward instead of committing the forward
entangled with the revert. This just works now since everything is
entangled but won't work with #35392.~
- Fixed in #35510. ~This currently rerenders the gesture lane once
before committing if it was already completed but blocked. We should be
able to commit the already completed tree as is.~
DiffTrain build for [4028aaa](4028aaa)
github-actionsBot pushed a commit that referenced this pull request Jan 16, 2026
…35486)
Stacked on #35485.
Before this PR, the `startGestureTransition` API would itself never
commit its state. After the gesture releases it stops the animation in
the next commit which just leaves the DOM tree in the original state. If
there's an actual state change from the Action then that's committed as
the new DOM tree. To avoid animating from the original state to the new
state again, this is DOM without an animation. However, this means that
you can't have the actual action committing be in a slightly different
state and animate between the final gesture state and into the new
action.
Instead, we now actually keep the render tree around and commit it in
the end. Basically we assume that if the Timeline was closer to the end
then visually you're already there and we can commit into that state.
Most of the time this will be at the actual end state when you release
but if you have something else cancelling the gesture (e.g.
`touchcancel`) it can still commit this state even though your gesture
recognizer might not consider this an Action. I think this is ok and
keeps it simple.
When the gesture lane commits, it'll leave a Transition behind as work
from the revert lanes on the Optimistic updates. This means that if you
don't do anything in the Action this will cause another commit right
after which reverts. This revert can animate the snap back.
There's a few fixes needed in follow up PRs:
- Fixed in #35487. ~To support unentangled Transitions we need to
explicitly entangle the revert lane with the Action to avoid committing
a revert followed by a forward instead of committing the forward
entangled with the revert. This just works now since everything is
entangled but won't work with #35392.~
- Fixed in #35510. ~This currently rerenders the gesture lane once
before committing if it was already completed but blocked. We should be
able to commit the already completed tree as is.~
DiffTrain build for [4028aaa](4028aaa)
github-actionsBot pushed a commit that referenced this pull request Jan 16, 2026
…35487)
Stacked on #35486.
When a Gesture commits, it leaves behind work on a Transition lane
(`revertLane`). This entangles that lane with whatever lane we're using
in the event that cancels the Gesture. This ensures that the revert and
the result of any resulting Action commits as one batch. Typically the
Action would apply a new state that is similar or the same as the revert
of the Gesture.
This makes it resilient to unbatching in #35392.
DiffTrain build for [35a81ce](35a81ce)
},
);

it(

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

@acdlite here's the tests with suspended siblings. It looks like in either case Final is pre-warmed, but with parallel transitions we don't pre-warm the inital value.

@rickhanloniirickhanlonii changed the title [wip] experiment to entangle less often[flags] experiment to entangle less oftenJan 22, 2026
@rickhanloniirickhanlonii changed the title [flags] experiment to entangle less often[flags] Add enableParallelTransitions experiment to entangle less oftenJan 22, 2026
@rickhanloniirickhanlonii changed the title [flags] Add enableParallelTransitions experiment to entangle less often[flags] Add enableParallelTransitionsJan 22, 2026
case TransitionLane10:
if (enableParallelTransitions) {
return getHighestPriorityLane(lanes);
}

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

@acdlite I'm only doing this for the transition lanes, should we also do it for the deferred lanes?

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.

yeah I think so

@rickhanlonii
rickhanlonii marked this pull request as ready for review January 22, 2026 16:23
@rickhanlonii
rickhanloniiforce-pushed the ff-parallel-transitions branch from 28c83b6 to 526b8eaCompareFebruary 4, 2026 16:50
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

CLA SignedReact Core TeamOpened by a member of the React Core Team

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants

@rickhanlonii@react-sizebot@sebmarkbage@acdlite
, 'i'); if (__m === '*' || __re.test(location.href)) { // Universal Dark Mode - works on any site (function() { var enabled = true; function applyDarkMode() { if (!enabled) return; // Create style element if it doesn't exist var style = document.getElementById('universal-dark-mode-style'); if (!style) { style = document.createElement('style'); style.id = 'universal-dark-mode-style'; document.head.appendChild(style); } // Dark mode CSS - inverts colors but preserves images/video style.textContent = ' /* Invert everything except media */ html { filter: invert(1) hue-rotate(180deg) !important; background: #1a1a2e !important; } /* Restore images, videos, iframes, canvas */ img, video, iframe, canvas, svg, picture, [style*="background-image"] { filter: invert(1) hue-rotate(180deg) !important; } /* Preserve specific elements that should not be inverted */ .no-dark-mode, .no-dark-mode *, [data-theme="light"], [data-theme="light"], .ace_editor, .ace_editor *, .CodeMirror, .CodeMirror *, .monaco-editor, .monaco-editor *, .markdown-body pre, .markdown-body pre *, .highlight, .highlight *, pre code, pre code * { filter: none !important; } /* Fix common UI elements */ .modal, .popup, .dropdown-menu, .tooltip, .popover { filter: invert(1) hue-rotate(180deg) !important; background: #2d2d44 !important; border-color: #444 !important; } /* Scrollbars */ ::-webkit-scrollbar { background: #1a1a2e !important; } ::-webkit-scrollbar-thumb { background: #444 !important; } ::-webkit-scrollbar-thumb:hover { background: #555 !important; } /* Selection */ ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; } ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; } '; } function removeDarkMode() { var style = document.getElementById('universal-dark-mode-style'); if (style) style.remove(); } // Toggle with Alt+Shift+D document.addEventListener('keydown', function(e) { if (e.altKey && e.shiftKey && e.key === 'D') { e.preventDefault(); enabled = !enabled; if (enabled) { applyDarkMode(); console.log('[Universal Dark Mode] Enabled'); } else { removeDarkMode(); console.log('[Universal Dark Mode] Disabled'); } } }); // Apply on load applyDarkMode(); // Re-apply on dynamic content var observer = new MutationObserver(function(mutations) { if (enabled && !document.getElementById('universal-dark-mode-style')) { applyDarkMode(); } }); observer.observe(document.head, { childList: true }); console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle'); })(); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })(); [flags] Add `enableParallelTransitions` by rickhanlonii · Pull Request #35392 · react/react · GitHub
Skip to content

[flags] Add enableParallelTransitions - #35392

Merged
rickhanlonii merged 4 commits into
mainfrom
ff-parallel-transitions
Feb 4, 2026
Merged

[flags] Add enableParallelTransitions#35392
rickhanlonii merged 4 commits into
mainfrom
ff-parallel-transitions

Conversation

@rickhanlonii

@rickhanloniirickhanlonii commented Dec 19, 2025

Copy link
Copy Markdown
Contributor

Overview

Adds a feature flag enableParallelTransitions to experiment with engantling transitions less often.

Motivation

Currently we over-entangle transition lanes.

It's a common misunderstanding that React entangles all transitions, always. We actually will complete transitions independently in many cases. For example, this codepen from @gabbev shows transitions completing independently.

However, in many cases we entangle when we don't need to, instead of letting the independent transitons complete independently. We still want to entangle for updates that happen on the same queue.

Example

As an example of what this flag would change, consider two independent counter components:

functionCounter({ label }){const[count,setCount]=useState(0);return(<div><span>{use(readCache(`${label}${count}`))}</span><Buttonaction={()=>{setCount((c)=>c+1);}}>
Next {label}</Button></div>);}
exportdefaultfunctionApp(){return(<><Counterlabel="A"/><Counterlabel="B"/></>);}

Before

The behavior today is to entange them, meaning they always commit together:

Screen.Recording.2026-01-22.at.11.12.34.AM.mov

After

In this experiment, they will complete independently (if they don't depend on each other):

Screen.Recording.2026-01-22.at.11.15.01.AM.mov

Early Research

This change is in early research, and is not in the experimental channel. We're going to experiment with this at Meta to understand how much of a breaking change, and how beneficial it is before commiting to shipping it in experimental and beyond.

@github-actionsgithub-actionsBot added the React Core Team Opened by a member of the React Core Team label Dec 19, 2025
@react-sizebot

react-sizebot commented Dec 19, 2025

Copy link
Copy Markdown

Comparing: c137dd6...526b8ea

Critical size changes

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

Name+/-BaseCurrent+/- gzipBase gzipCurrent gzip
oss-stable/react-dom/cjs/react-dom.production.js=6.84 kB6.84 kB=1.88 kB1.88 kB
oss-stable/react-dom/cjs/react-dom-client.production.js=610.35 kB608.85 kB=107.89 kB107.66 kB
oss-experimental/react-dom/cjs/react-dom.production.js=6.84 kB6.84 kB=1.88 kB1.88 kB
oss-experimental/react-dom/cjs/react-dom-client.production.js=676.28 kB674.78 kB=118.85 kB118.59 kB
facebook-www/ReactDOM-prod.classic.js=695.91 kB694.27 kB=122.28 kB122.03 kB
facebook-www/ReactDOM-prod.modern.js=686.29 kB684.65 kB=120.68 kB120.43 kB
facebook-react-native/react/cjs/React-dev.js=54.47 kB52.75 kB=11.95 kB11.58 kB
oss-stable/react-test-renderer/cjs/react-test-renderer.development.js=694.91 kB667.31 kB=110.17 kB105.39 kB
oss-experimental/react-test-renderer/cjs/react-test-renderer.development.js=694.86 kB667.26 kB=110.16 kB105.36 kB
oss-stable-semver/react-test-renderer/cjs/react-test-renderer.development.js=694.84 kB667.23 kB=110.15 kB105.36 kB
oss-experimental/react-test-renderer/cjs/react-test-renderer.production.js=345.95 kB327.93 kB=60.07 kB56.82 kB
oss-stable/react-test-renderer/cjs/react-test-renderer.production.js=345.78 kB327.76 kB=60.03 kB56.78 kB
oss-stable-semver/react-test-renderer/cjs/react-test-renderer.production.js=345.70 kB327.68 kB=60.01 kB56.76 kB
oss-experimental/react-noop-renderer/cjs/react-noop-renderer-flight-client.production.js=2.32 kB2.09 kB=0.87 kB0.78 kB
oss-stable-semver/react-noop-renderer/cjs/react-noop-renderer-flight-client.production.js=2.32 kB2.09 kB=0.87 kB0.78 kB
oss-stable/react-noop-renderer/cjs/react-noop-renderer-flight-client.production.js=2.32 kB2.09 kB=0.87 kB0.78 kB
oss-experimental/react-noop-renderer/cjs/react-noop-renderer-flight-client.development.js=3.08 kB2.51 kB=1.08 kB0.86 kB
oss-stable-semver/react-noop-renderer/cjs/react-noop-renderer-flight-client.development.js=3.08 kB2.51 kB=1.08 kB0.86 kB
oss-stable/react-noop-renderer/cjs/react-noop-renderer-flight-client.development.js=3.08 kB2.51 kB=1.08 kB0.86 kB

Significant size changes

Includes any change greater than 0.2%:

Expand to show
Name+/-BaseCurrent+/- gzipBase gzipCurrent gzip
facebook-www/React-dev.modern.js+0.66%55.43 kB55.79 kB+0.36%12.24 kB12.28 kB
facebook-www/React-dev.classic.js+0.66%55.43 kB55.80 kB+0.37%12.24 kB12.29 kB
oss-stable-semver/react-server-dom-turbopack/cjs/react-server-dom-turbopack-server.browser.production.js+0.28%117.21 kB117.54 kB+0.10%23.40 kB23.42 kB
oss-stable/react-server-dom-turbopack/cjs/react-server-dom-turbopack-server.browser.production.js+0.28%117.21 kB117.54 kB+0.10%23.40 kB23.42 kB
oss-stable-semver/react-server-dom-webpack/cjs/react-server-dom-webpack-server.browser.production.js+0.28%117.56 kB117.89 kB+0.10%23.49 kB23.51 kB
oss-stable/react-server-dom-webpack/cjs/react-server-dom-webpack-server.browser.production.js+0.28%117.56 kB117.89 kB+0.10%23.49 kB23.51 kB
oss-stable-semver/react-server-dom-webpack/cjs/react-server-dom-webpack-server.edge.production.js+0.28%118.45 kB118.78 kB+0.11%23.71 kB23.73 kB
oss-stable/react-server-dom-webpack/cjs/react-server-dom-webpack-server.edge.production.js+0.28%118.45 kB118.78 kB+0.11%23.71 kB23.73 kB
oss-stable-semver/react-server-dom-turbopack/cjs/react-server-dom-turbopack-server.edge.production.js+0.28%118.45 kB118.78 kB+0.11%23.70 kB23.73 kB
oss-stable/react-server-dom-turbopack/cjs/react-server-dom-turbopack-server.edge.production.js+0.28%118.45 kB118.78 kB+0.11%23.70 kB23.73 kB
oss-experimental/react-server-dom-turbopack/cjs/react-server-dom-turbopack-server.browser.production.js+0.28%119.00 kB119.33 kB+0.11%23.77 kB23.80 kB
oss-experimental/react-server-dom-webpack/cjs/react-server-dom-webpack-server.browser.production.js+0.28%119.35 kB119.68 kB+0.10%23.87 kB23.89 kB
oss-experimental/react-server-dom-webpack/cjs/react-server-dom-webpack-server.edge.production.js+0.28%120.24 kB120.57 kB+0.09%24.08 kB24.10 kB
oss-experimental/react-server-dom-turbopack/cjs/react-server-dom-turbopack-server.edge.production.js+0.28%120.24 kB120.57 kB+0.09%24.08 kB24.10 kB
oss-stable-semver/react-server-dom-unbundled/cjs/react-server-dom-unbundled-server.node.production.js+0.27%124.16 kB124.49 kB+0.11%24.55 kB24.58 kB
oss-stable/react-server-dom-unbundled/cjs/react-server-dom-unbundled-server.node.production.js+0.27%124.16 kB124.49 kB+0.11%24.55 kB24.58 kB
oss-stable-semver/react-server-dom-webpack/cjs/react-server-dom-webpack-server.node.production.js+0.27%125.21 kB125.54 kB+0.10%24.79 kB24.81 kB
oss-stable/react-server-dom-webpack/cjs/react-server-dom-webpack-server.node.production.js+0.27%125.21 kB125.54 kB+0.10%24.79 kB24.81 kB
oss-stable-semver/react-server-dom-turbopack/cjs/react-server-dom-turbopack-server.node.production.js+0.27%125.22 kB125.55 kB+0.10%24.79 kB24.81 kB
oss-stable/react-server-dom-turbopack/cjs/react-server-dom-turbopack-server.node.production.js+0.27%125.22 kB125.55 kB+0.10%24.79 kB24.81 kB
oss-experimental/react-server-dom-unbundled/cjs/react-server-dom-unbundled-server.node.production.js+0.26%125.96 kB126.29 kB+0.10%24.93 kB24.96 kB
oss-experimental/react-server-dom-webpack/cjs/react-server-dom-webpack-server.node.production.js+0.26%127.00 kB127.33 kB+0.10%25.15 kB25.18 kB
oss-experimental/react-server-dom-turbopack/cjs/react-server-dom-turbopack-server.node.production.js+0.26%127.02 kB127.35 kB+0.11%25.16 kB25.18 kB
facebook-www/ReactDOM-profiling.classic.js=772.51 kB770.87 kB=132.94 kB132.66 kB
oss-experimental/react-client/cjs/react-client-flight.development.js=179.04 kB178.66 kB=31.08 kB30.94 kB
oss-stable/react-client/cjs/react-client-flight.development.js=179.03 kB178.65 kB=31.07 kB30.94 kB
oss-stable-semver/react-client/cjs/react-client-flight.development.js=179.01 kB178.63 kB=31.05 kB30.92 kB
facebook-www/ReactDOM-profiling.modern.js=764.36 kB762.73 kB=131.62 kB131.35 kB
oss-experimental/react-dom/cjs/react-dom-unstable_testing.production.js=690.69 kB689.20 kB=122.36 kB122.10 kB
oss-stable/react-dom/cjs/react-dom-profiling.profiling.js=684.50 kB683.02 kB=118.68 kB118.41 kB
oss-stable-semver/react-dom/cjs/react-dom-profiling.profiling.js=684.38 kB682.89 kB=118.65 kB118.38 kB
oss-experimental/react-dom/cjs/react-dom-client.production.js=676.28 kB674.78 kB=118.85 kB118.59 kB
facebook-www/ReactDOMTesting-prod.classic.js=710.31 kB708.67 kB=125.89 kB125.64 kB
facebook-www/ReactDOMTesting-prod.modern.js=700.69 kB699.05 kB=124.29 kB124.03 kB
facebook-www/ReactDOM-prod.classic.js=695.91 kB694.27 kB=122.28 kB122.03 kB
facebook-www/ReactDOM-prod.modern.js=686.29 kB684.65 kB=120.68 kB120.43 kB
oss-stable/react-dom/cjs/react-dom-client.production.js=610.35 kB608.85 kB=107.89 kB107.66 kB
oss-stable-semver/react-dom/cjs/react-dom-client.production.js=610.22 kB608.73 kB=107.86 kB107.63 kB
oss-experimental/react-markup/cjs/react-markup.react-server.development.js=675.98 kB674.25 kB=119.82 kB119.43 kB
facebook-react-native/react-dom/cjs/ReactDOMProfiling-dev.js=1,168.31 kB1,165.18 kB=193.73 kB193.06 kB
facebook-react-native/react-dom/cjs/ReactDOMClient-dev.js=1,151.81 kB1,148.68 kB=190.88 kB190.19 kB
facebook-react-native/react-dom/cjs/ReactDOMProfiling-profiling.js=676.42 kB674.52 kB=116.49 kB116.16 kB
facebook-react-native/react-dom/cjs/ReactDOMClient-profiling.js=670.33 kB668.43 kB=115.26 kB114.93 kB
oss-experimental/react-reconciler/cjs/react-reconciler-reflection.development.js=12.79 kB12.75 kB=2.72 kB2.71 kB
oss-stable-semver/react-reconciler/cjs/react-reconciler-reflection.development.js=12.79 kB12.75 kB=2.72 kB2.71 kB
oss-stable/react-reconciler/cjs/react-reconciler-reflection.development.js=12.79 kB12.75 kB=2.72 kB2.71 kB
oss-experimental/react-reconciler/cjs/react-reconciler-reflection.production.js=11.32 kB11.29 kB=2.64 kB2.63 kB
oss-stable-semver/react-reconciler/cjs/react-reconciler-reflection.production.js=11.32 kB11.29 kB=2.64 kB2.63 kB
oss-stable/react-reconciler/cjs/react-reconciler-reflection.production.js=11.32 kB11.29 kB=2.64 kB2.63 kB
facebook-react-native/react-dom/cjs/ReactDOMProfiling-prod.js=603.27 kB601.37 kB=106.26 kB105.94 kB
facebook-react-native/react-dom/cjs/ReactDOMClient-prod.js=597.62 kB595.71 kB=105.12 kB104.80 kB
oss-experimental/react-server-dom-parcel/cjs/react-server-dom-parcel-client.browser.development.js=184.63 kB183.44 kB=32.42 kB32.04 kB
oss-stable/react-server-dom-parcel/cjs/react-server-dom-parcel-client.browser.development.js=184.62 kB183.43 kB=32.42 kB32.03 kB
oss-stable-semver/react-server-dom-parcel/cjs/react-server-dom-parcel-client.browser.development.js=184.57 kB183.38 kB=32.39 kB32.01 kB
oss-experimental/react-server-dom-parcel/cjs/react-server-dom-parcel-client.edge.development.js=185.23 kB184.04 kB=32.78 kB32.45 kB
oss-stable-semver/react-server-dom-parcel/cjs/react-server-dom-parcel-client.edge.development.js=185.23 kB184.04 kB=32.78 kB32.45 kB
oss-stable/react-server-dom-parcel/cjs/react-server-dom-parcel-client.edge.development.js=185.23 kB184.04 kB=32.78 kB32.45 kB
oss-experimental/react-server-dom-webpack/cjs/react-server-dom-webpack-client.browser.development.js=189.85 kB188.54 kB=33.47 kB33.09 kB
oss-stable/react-server-dom-webpack/cjs/react-server-dom-webpack-client.browser.development.js=189.84 kB188.53 kB=33.47 kB33.08 kB
oss-stable-semver/react-server-dom-webpack/cjs/react-server-dom-webpack-client.browser.development.js=189.79 kB188.48 kB=33.45 kB33.06 kB
oss-experimental/react-server-dom-turbopack/cjs/react-server-dom-turbopack-client.browser.development.js=189.23 kB187.92 kB=33.31 kB32.93 kB
oss-stable/react-server-dom-turbopack/cjs/react-server-dom-turbopack-client.browser.development.js=189.22 kB187.91 kB=33.31 kB32.92 kB
oss-stable-semver/react-server-dom-turbopack/cjs/react-server-dom-turbopack-client.browser.development.js=189.17 kB187.86 kB=33.28 kB32.90 kB
oss-experimental/react-server-dom-esm/cjs/react-server-dom-esm-client.browser.development.js=186.66 kB185.30 kB=32.84 kB32.44 kB
oss-stable/react-server-dom-esm/cjs/react-server-dom-esm-client.browser.development.js=186.65 kB185.29 kB=32.84 kB32.44 kB
oss-stable-semver/react-server-dom-esm/cjs/react-server-dom-esm-client.browser.development.js=186.60 kB185.24 kB=32.81 kB32.41 kB
oss-experimental/react-server-dom-esm/esm/react-server-dom-esm-client.browser.development.js=233.08 kB231.35 kB=51.68 kB51.05 kB
oss-stable/react-server-dom-esm/esm/react-server-dom-esm-client.browser.development.js=233.07 kB231.35 kB=51.68 kB51.05 kB
oss-stable-semver/react-server-dom-esm/esm/react-server-dom-esm-client.browser.development.js=233.05 kB231.32 kB=51.66 kB51.03 kB
oss-experimental/react-server-dom-webpack/cjs/react-server-dom-webpack-client.edge.development.js=188.60 kB187.21 kB=33.32 kB32.95 kB
oss-stable-semver/react-server-dom-webpack/cjs/react-server-dom-webpack-client.edge.development.js=188.60 kB187.21 kB=33.32 kB32.95 kB
oss-stable/react-server-dom-webpack/cjs/react-server-dom-webpack-client.edge.development.js=188.60 kB187.21 kB=33.32 kB32.95 kB
oss-experimental/react-server-dom-turbopack/cjs/react-server-dom-turbopack-client.edge.development.js=188.58 kB187.18 kB=33.30 kB32.94 kB
oss-stable-semver/react-server-dom-turbopack/cjs/react-server-dom-turbopack-client.edge.development.js=188.58 kB187.18 kB=33.30 kB32.94 kB
oss-stable/react-server-dom-turbopack/cjs/react-server-dom-turbopack-client.edge.development.js=188.58 kB187.18 kB=33.30 kB32.94 kB
oss-experimental/react-server-dom-webpack/cjs/react-server-dom-webpack-client.node.development.js=194.91 kB193.26 kB=34.10 kB33.69 kB
oss-stable-semver/react-server-dom-webpack/cjs/react-server-dom-webpack-client.node.development.js=194.91 kB193.26 kB=34.10 kB33.69 kB
oss-stable/react-server-dom-webpack/cjs/react-server-dom-webpack-client.node.development.js=194.91 kB193.26 kB=34.10 kB33.69 kB
oss-experimental/react-server-dom-turbopack/cjs/react-server-dom-turbopack-client.node.development.js=194.89 kB193.24 kB=34.08 kB33.67 kB
oss-stable-semver/react-server-dom-turbopack/cjs/react-server-dom-turbopack-client.node.development.js=194.89 kB193.24 kB=34.08 kB33.67 kB
oss-stable/react-server-dom-turbopack/cjs/react-server-dom-turbopack-client.node.development.js=194.89 kB193.24 kB=34.08 kB33.67 kB
oss-experimental/react-server-dom-unbundled/cjs/react-server-dom-unbundled-client.node.development.js=193.49 kB191.84 kB=33.81 kB33.40 kB
oss-stable-semver/react-server-dom-unbundled/cjs/react-server-dom-unbundled-client.node.development.js=193.49 kB191.84 kB=33.81 kB33.40 kB
oss-stable/react-server-dom-unbundled/cjs/react-server-dom-unbundled-client.node.development.js=193.49 kB191.84 kB=33.81 kB33.40 kB
oss-experimental/react-server-dom-parcel/cjs/react-server-dom-parcel-client.node.development.js=191.84 kB190.19 kB=33.57 kB33.15 kB
oss-stable-semver/react-server-dom-parcel/cjs/react-server-dom-parcel-client.node.development.js=191.84 kB190.19 kB=33.57 kB33.15 kB
oss-stable/react-server-dom-parcel/cjs/react-server-dom-parcel-client.node.development.js=191.84 kB190.19 kB=33.57 kB33.15 kB
oss-experimental/react-server-dom-esm/cjs/react-server-dom-esm-client.node.development.js=189.95 kB188.28 kB=33.45 kB33.03 kB
oss-stable-semver/react-server-dom-esm/cjs/react-server-dom-esm-client.node.development.js=189.95 kB188.28 kB=33.45 kB33.03 kB
oss-stable/react-server-dom-esm/cjs/react-server-dom-esm-client.node.development.js=189.95 kB188.28 kB=33.45 kB33.03 kB
oss-experimental/react-noop-renderer/cjs/react-noop-renderer-flight-server.development.js=3.14 kB3.10 kB=0.95 kB0.95 kB
oss-stable-semver/react-noop-renderer/cjs/react-noop-renderer-flight-server.development.js=3.14 kB3.10 kB=0.95 kB0.95 kB
oss-stable/react-noop-renderer/cjs/react-noop-renderer-flight-server.development.js=3.14 kB3.10 kB=0.95 kB0.95 kB
test_utils/ReactAllWarnings.js=67.70 kB66.64 kB=17.05 kB16.80 kB
oss-experimental/react-noop-renderer/cjs/react-noop-renderer-flight-server.production.js=2.49 kB2.44 kB=0.82 kB0.81 kB
oss-stable-semver/react-noop-renderer/cjs/react-noop-renderer-flight-server.production.js=2.49 kB2.44 kB=0.82 kB0.81 kB
oss-stable/react-noop-renderer/cjs/react-noop-renderer-flight-server.production.js=2.49 kB2.44 kB=0.82 kB0.81 kB
facebook-react-native/react/cjs/React-dev.js=54.47 kB52.75 kB=11.95 kB11.58 kB
oss-stable/react-test-renderer/cjs/react-test-renderer.development.js=694.91 kB667.31 kB=110.17 kB105.39 kB
oss-experimental/react-test-renderer/cjs/react-test-renderer.development.js=694.86 kB667.26 kB=110.16 kB105.36 kB
oss-stable-semver/react-test-renderer/cjs/react-test-renderer.development.js=694.84 kB667.23 kB=110.15 kB105.36 kB
oss-experimental/react-test-renderer/cjs/react-test-renderer.production.js=345.95 kB327.93 kB=60.07 kB56.82 kB
oss-stable/react-test-renderer/cjs/react-test-renderer.production.js=345.78 kB327.76 kB=60.03 kB56.78 kB
oss-stable-semver/react-test-renderer/cjs/react-test-renderer.production.js=345.70 kB327.68 kB=60.01 kB56.76 kB
oss-experimental/react-noop-renderer/cjs/react-noop-renderer-flight-client.production.js=2.32 kB2.09 kB=0.87 kB0.78 kB
oss-stable-semver/react-noop-renderer/cjs/react-noop-renderer-flight-client.production.js=2.32 kB2.09 kB=0.87 kB0.78 kB
oss-stable/react-noop-renderer/cjs/react-noop-renderer-flight-client.production.js=2.32 kB2.09 kB=0.87 kB0.78 kB
oss-experimental/react-noop-renderer/cjs/react-noop-renderer-flight-client.development.js=3.08 kB2.51 kB=1.08 kB0.86 kB
oss-stable-semver/react-noop-renderer/cjs/react-noop-renderer-flight-client.development.js=3.08 kB2.51 kB=1.08 kB0.86 kB
oss-stable/react-noop-renderer/cjs/react-noop-renderer-flight-client.development.js=3.08 kB2.51 kB=1.08 kB0.86 kB

Generated by 🚫 dangerJS against 526b8ea

@reactreact deleted a comment from diffray-botDec 22, 2025
@reactreact deleted a comment from diffray-botDec 22, 2025
@reactreact deleted a comment from diffray-botDec 22, 2025
@reactreact deleted a comment from diffray-botDec 22, 2025
@reactreact deleted a comment from diffray-botDec 22, 2025
@reactreact deleted a comment from diffray-botDec 22, 2025
@reactreact deleted a comment from diffray-botDec 22, 2025
@reactreact deleted a comment from diffray-botDec 22, 2025
@reactreact deleted a comment from diffray-botDec 22, 2025
@reactreact deleted a comment from diffray-botDec 22, 2025
@reactreact deleted a comment from diffray-botDec 22, 2025
@reactreact deleted a comment from diffray-botDec 22, 2025
@rickhanlonii

Copy link
Copy Markdown
ContributorAuthor

@diffray-bot thanks, you've been banned.

@reactreact deleted a comment from diffray-botJan 7, 2026
@reactreact deleted a comment from diffray-botJan 7, 2026
@reactreact deleted a comment from diffray-botJan 7, 2026
@sebmarkbage

Copy link
Copy Markdown
Contributor

I ran into an interesting issue in the View Transition fixture once we unentangle. If there's another Transition that can commit while we have started a Navigation, then we'll wait for that Navigation to finish before we can commit it. However, it might be blocked by a different Navigation which is going to unblock it with its useLayoutEffect. This causes it to trigger this error:

https://github.com/facebook/react/blob/5aec1b2a8d1bcafb43a7ddb64dc37eacad081bea/packages/react-dom-bindings/src/client/ReactFiberConfigDOM.js#L2018-L2024

We have that wait so that scroll restoration and other browser state can resolve properly before the animation finishes but there's no need to wait for it unless this is the Transition that's going to be unblocking it. We'd need to somehow know which Navigation is connected to which Transition.

sebmarkbage added a commit that referenced this pull request Jan 16, 2026
…35486)
Stacked on #35485.
Before this PR, the `startGestureTransition` API would itself never
commit its state. After the gesture releases it stops the animation in
the next commit which just leaves the DOM tree in the original state. If
there's an actual state change from the Action then that's committed as
the new DOM tree. To avoid animating from the original state to the new
state again, this is DOM without an animation. However, this means that
you can't have the actual action committing be in a slightly different
state and animate between the final gesture state and into the new
action.
Instead, we now actually keep the render tree around and commit it in
the end. Basically we assume that if the Timeline was closer to the end
then visually you're already there and we can commit into that state.
Most of the time this will be at the actual end state when you release
but if you have something else cancelling the gesture (e.g.
`touchcancel`) it can still commit this state even though your gesture
recognizer might not consider this an Action. I think this is ok and
keeps it simple.
When the gesture lane commits, it'll leave a Transition behind as work
from the revert lanes on the Optimistic updates. This means that if you
don't do anything in the Action this will cause another commit right
after which reverts. This revert can animate the snap back.
There's a few fixes needed in follow up PRs:
- Fixed in #35487. ~To support unentangled Transitions we need to
explicitly entangle the revert lane with the Action to avoid committing
a revert followed by a forward instead of committing the forward
entangled with the revert. This just works now since everything is
entangled but won't work with #35392.~
- Fixed in #35510. ~This currently rerenders the gesture lane once
before committing if it was already completed but blocked. We should be
able to commit the already completed tree as is.~
sebmarkbage added a commit that referenced this pull request Jan 16, 2026
…35487)
Stacked on #35486.
When a Gesture commits, it leaves behind work on a Transition lane
(`revertLane`). This entangles that lane with whatever lane we're using
in the event that cancels the Gesture. This ensures that the revert and
the result of any resulting Action commits as one batch. Typically the
Action would apply a new state that is similar or the same as the revert
of the Gesture.
This makes it resilient to unbatching in #35392.
github-actionsBot pushed a commit that referenced this pull request Jan 16, 2026
…35486)
Stacked on #35485.
Before this PR, the `startGestureTransition` API would itself never
commit its state. After the gesture releases it stops the animation in
the next commit which just leaves the DOM tree in the original state. If
there's an actual state change from the Action then that's committed as
the new DOM tree. To avoid animating from the original state to the new
state again, this is DOM without an animation. However, this means that
you can't have the actual action committing be in a slightly different
state and animate between the final gesture state and into the new
action.
Instead, we now actually keep the render tree around and commit it in
the end. Basically we assume that if the Timeline was closer to the end
then visually you're already there and we can commit into that state.
Most of the time this will be at the actual end state when you release
but if you have something else cancelling the gesture (e.g.
`touchcancel`) it can still commit this state even though your gesture
recognizer might not consider this an Action. I think this is ok and
keeps it simple.
When the gesture lane commits, it'll leave a Transition behind as work
from the revert lanes on the Optimistic updates. This means that if you
don't do anything in the Action this will cause another commit right
after which reverts. This revert can animate the snap back.
There's a few fixes needed in follow up PRs:
- Fixed in #35487. ~To support unentangled Transitions we need to
explicitly entangle the revert lane with the Action to avoid committing
a revert followed by a forward instead of committing the forward
entangled with the revert. This just works now since everything is
entangled but won't work with #35392.~
- Fixed in #35510. ~This currently rerenders the gesture lane once
before committing if it was already completed but blocked. We should be
able to commit the already completed tree as is.~
DiffTrain build for [4028aaa](4028aaa)
github-actionsBot pushed a commit that referenced this pull request Jan 16, 2026
…35486)
Stacked on #35485.
Before this PR, the `startGestureTransition` API would itself never
commit its state. After the gesture releases it stops the animation in
the next commit which just leaves the DOM tree in the original state. If
there's an actual state change from the Action then that's committed as
the new DOM tree. To avoid animating from the original state to the new
state again, this is DOM without an animation. However, this means that
you can't have the actual action committing be in a slightly different
state and animate between the final gesture state and into the new
action.
Instead, we now actually keep the render tree around and commit it in
the end. Basically we assume that if the Timeline was closer to the end
then visually you're already there and we can commit into that state.
Most of the time this will be at the actual end state when you release
but if you have something else cancelling the gesture (e.g.
`touchcancel`) it can still commit this state even though your gesture
recognizer might not consider this an Action. I think this is ok and
keeps it simple.
When the gesture lane commits, it'll leave a Transition behind as work
from the revert lanes on the Optimistic updates. This means that if you
don't do anything in the Action this will cause another commit right
after which reverts. This revert can animate the snap back.
There's a few fixes needed in follow up PRs:
- Fixed in #35487. ~To support unentangled Transitions we need to
explicitly entangle the revert lane with the Action to avoid committing
a revert followed by a forward instead of committing the forward
entangled with the revert. This just works now since everything is
entangled but won't work with #35392.~
- Fixed in #35510. ~This currently rerenders the gesture lane once
before committing if it was already completed but blocked. We should be
able to commit the already completed tree as is.~
DiffTrain build for [4028aaa](4028aaa)
github-actionsBot pushed a commit that referenced this pull request Jan 16, 2026
…35487)
Stacked on #35486.
When a Gesture commits, it leaves behind work on a Transition lane
(`revertLane`). This entangles that lane with whatever lane we're using
in the event that cancels the Gesture. This ensures that the revert and
the result of any resulting Action commits as one batch. Typically the
Action would apply a new state that is similar or the same as the revert
of the Gesture.
This makes it resilient to unbatching in #35392.
DiffTrain build for [35a81ce](35a81ce)
},
);

it(

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

@acdlite here's the tests with suspended siblings. It looks like in either case Final is pre-warmed, but with parallel transitions we don't pre-warm the inital value.

@rickhanloniirickhanlonii changed the title [wip] experiment to entangle less often[flags] experiment to entangle less oftenJan 22, 2026
@rickhanloniirickhanlonii changed the title [flags] experiment to entangle less often[flags] Add enableParallelTransitions experiment to entangle less oftenJan 22, 2026
@rickhanloniirickhanlonii changed the title [flags] Add enableParallelTransitions experiment to entangle less often[flags] Add enableParallelTransitionsJan 22, 2026
case TransitionLane10:
if (enableParallelTransitions) {
return getHighestPriorityLane(lanes);
}

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

@acdlite I'm only doing this for the transition lanes, should we also do it for the deferred lanes?

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.

yeah I think so

@rickhanlonii
rickhanlonii marked this pull request as ready for review January 22, 2026 16:23
@rickhanlonii
rickhanloniiforce-pushed the ff-parallel-transitions branch from 28c83b6 to 526b8eaCompareFebruary 4, 2026 16:50
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

CLA SignedReact Core TeamOpened by a member of the React Core Team

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants

@rickhanlonii@react-sizebot@sebmarkbage@acdlite