Upgrade to React 19#61521
Conversation
|
It's odd that we see some warnings in the unit test on this PR but not when I did the React 18.3 upgrade. |
|
Indeed. Maybe they tinkered with some feature flags between 18.3 and 19 beta. |
|
If I understand correctly, many of the test warnings seem to be because of how Ariakit retrieves the ref prop, it falls back to and that yields a warning in React 19. At the same time, I assume Ariakit will stay compatible with older React versions where @diegohaz @DaniGuardiola any idea if we're planning to do anything about these warnings in Reakit? |
|
@diegohaz could Ariakit possibly use some sort of React version detection instead and use it to determine what to do, rather than this fallback system that creates warnings? |
|
That part was updated to avoid those warnings (ref: https://x.com/diegohaz/status/1780794744534163888). But apparently it's not enough? I still don't know the idiomatic way to fix that. |
|
@diegohaz @DaniGuardiola I actually tried updating to the latest Ariakit version in this branch and it seems to resolve those warnings 👍 Dani has started working on the update in #60992, and here we have one more reason to move it forward. |
|
Updated to the RC. Hoping to have some time next week to play more thoroughly with it. |
|
Size Change: +15.1 kB (+0.19%) Total Size: 8.05 MB 📦 View Changed
ℹ️ View Unchanged
|
|
Fixed some tests, most of them were pretty straightforward. At first glance, the mobile tests seem to be failing because RN uses an old React version - we'll need a RN update to support React 19. |
|
FYI, Framer Motion will be a blocker for now. More context: #60975 |
|
React 19.0.0-rc.0. is out.
I also wonder if facebook/react#29585 could be an issue for the block editor. Potentially, it can affect block re-ordering, but it will need some testing. |
|
Seems like we'll need to wait a bit for now, re https://x.com/sophiebits/status/1801663976973209620?s=46 and https://tkdodo.eu/blog/react-19-and-suspense-a-drama-in-3-acts Happy to retry this once the release gets unblocked once more. |
|
The suspense issue doesn’t affect our codebase, so we could try the latest RC. |
|
Updated to the latest RC. We'll still need to wait for RN support: |
|
See callstack/react-native-testing-library#1698 for RN support for React 19 (hopefully H1 2025) |
8424167 to
63ac11d
Compare
|
A few updates as I've been experimenting with making React 19 work in the meantime:
|
|
I'm experimenting with using |
IIRC, we can also use suspense around components loading assets on demand and defer rendering. It could help render different previews. |
|
The |
6a9ada3 to
0ae8c13
Compare
Thanks for the heads up. A few updates:
Haven't done thorough testing, but it could be in a testable state at this point. Next steps: There are some failing e2e tests, we need to investigate why and fix the cause. Note: React Native is still a blocker: callstack/react-native-testing-library#1698 |
|
React v19 is now stable - https://react.dev/blog/2024/12/05/react-19. It seems that the biggest blocker here would be React Native. |
31f99c3 to
da65652
Compare
packages/grid kept React 18 in its peerDependencies, which forced npm to install a nested React 18 inside widgets/activity and widgets/welcome even though the rest of the tree is on 19.2.4. Bump grid's peerDeps and drop the unnecessary direct `react` dep from the two widgets — other widgets already rely on the top-level React. Also restores the nested React 18.3.1 install for test/native (still pinned to 18 for React Native compatibility) that was missing from the rebase's auto-merged lockfile.
React 19 treats `inert` as a boolean attribute, not a string. The
widget-chrome and widget-picker components were passing `inert: ''`,
which React 19 rejects with a console.error. Replace with the existing
branch pattern: `inert={ condition || undefined }` so the attribute is
omitted entirely when not active.
React 19 removed the no-argument overload of useRef. Each affected call now passes \`undefined\` explicitly, widening the ref type to include \`undefined\` where it wasn't already. Touches new media-editor, grid, and keyboard-shortcuts code that landed on trunk after the React 19 branch was originally written.
React 19 no longer declares JSX as a global namespace. The media-editor module's component-prop type definitions used \`JSX.Element\` directly; import \`JSX\` alongside the other named types from 'react'.
React 19 expects \`inert\` to be a boolean, not a string. The
dashboard-grid and dashboard-lanes item components were passing
\`{ inert: '' }\` via spread; replace with \`inert={ dragging || undefined }\`
matching the branch's established pattern (omit the attribute when
inactive rather than emit \`inert="false"\`).
React 19's cloneElement second-arg type requires a type-compatible
props shape. Cast the slot to \`ReactElement<{ children?: ReactNode }>\`
so the children injection type-checks (matches the function's
documented intent of overwriting any subtree passed on the slot).
React 19's stricter \`React.ReactElement\` typing leaves \`child.props\`
as \`unknown\`, so destructuring or passing \`actionableArea\` through
\`cloneElement\` fails. Cast the child to
\`React.ReactElement<{ actionableArea?: React.ReactNode }>\` so the
lift-and-strip pattern in DashboardGrid type-checks.
Both packages bumped their React peerDependencies from ^18 to ^19.2.4 as part of the broader React 19 upgrade; add the corresponding "Upgrade React to v19" entry under "Unreleased" so the per-package changelog checks pass.
This will probably be a race condition in collaboration cursor sync ( |
The post is looking great, thank you 🚀 |
|
Turns out there's a race condition when joining: awareness can arrive before the new block lands in the DOM. I think the fix should be simple: subscribe to the block tree and add it as an effect dependency. I'll push it here and work on moving it to an independent PR - #78636 |
A remote block insert can land in the DOM after the inserting user's cursor awareness arrives. Without a block-tree dependency, the overlay queries [data-block] once, finds nothing, and never retries — leaving the remote cursor invisible until the collaborator moves again.
jsnajdr
left a comment
There was a problem hiding this comment.
This is now ready to merge, let's go 🤞
|
It took just two years. Thanks for the dedication folks. |
|
Great job getting this over the line 👏 I noticed it had an effect on some performance metrics -
Both are inserter metrics. It's not obvious from the diff what it might be. |
We started looking at this with @ellatrix, but have no results yet. Most likely the thing that we measure (time to expand a button, time consumed by |
That's my expectation as well. Both Before optimizing anything, it'd be great to confirm whether perceived performance actually regressed. The cleanest signal would be INP (click > inserter visibly painted), which the perf harness can already capture via @jsnajdr @ellatrix, since you've already started digging here, would either of you be up for taking this angle? Happy to help with whatever I can. |
What?
Contains work to add React 19 support.
Not intended to be merged directly, but rather to serve as a container for experimentation with all necessary updates.
See #71336 for tracking the migration effort.
Why?
Keeping up to date with our latest framework dependencies and offering all the latest features.
How?
Bumping deps and working on various fixes to ensure React 19 compat. Don't merge.
Testing Instructions
Testing Instructions for Keyboard
Same as above
Screenshots or screencast
None