Skip to content

Upgrade React to 19 and WordPress packages to React 19-compatible versions#3642

Merged
wojtekn merged 7 commits into
trunkfrom
update-react-19-wordpress-packages
May 29, 2026
Merged

Upgrade React to 19 and WordPress packages to React 19-compatible versions#3642
wojtekn merged 7 commits into
trunkfrom
update-react-19-wordpress-packages

Conversation

@wojtekn
Copy link
Copy Markdown
Contributor

@wojtekn wojtekn commented May 29, 2026

Related issues

Closes #3590
Closes #3637

How AI was used in this PR

This PR was created with Claude Code. All changes were reviewed and verified with passing typecheck and full test suite.

Proposed Changes

This PR combines two Dependabot PRs (#3590 and #3637) that have a circular dependency — the new @wordpress/components@34, @wordpress/element@7, @wordpress/compose@8, and @wordpress/dataviews@15 all require React 19 as a peer dependency, making them impossible to land independently.

Package upgrades:

  • react / react-dom: 18.2.0 → 19.2.6
  • @types/react / @types/react-dom: 18.x → 19.x
  • @wordpress/components: 33.0.0 → 34.0.0
  • @wordpress/compose: 7.36.0 → 8.0.0
  • @wordpress/element: 6.39.0 → 7.0.0
  • @wordpress/dataviews: 14.2.0 → 15.0.0
  • @wordpress/icons: 13.1.0 → 13.2.0 (unpinned — the prior pin was a pre-React 19 workaround)
  • @wordpress/i18n, @wordpress/react-i18n, and several apps/ui WP packages bumped
  • @rive-app/react-canvas: 4.12.0 → 4.18.0 (adds React 19 peer dep support)
  • date-fns: ^3.3.1 → ^4.1.0 in tools/common and apps/studio — required so @wordpress/components@34

Patch file:

  • Renamed @wordpress+components+33.0.0.patch@wordpress+components+34.0.0.patch

TypeScript fixes for React 19 breaking changes:

  • useRef<T>()useRef<T>(undefined) (no-arg overload removed for non-DOM refs)
  • ForwardRefRenderFunction second param updated to React.ForwardedRef<T>
  • RefObject<T | null> propagated through prop types (React 19 useRef(null) is now nullable)
  • Dropped ref from react-markdown ExtraProps spreads to avoid v18/v19 LegacyRef mismatch
  • allowpopups="true"allowpopups={true} (boolean attribute)
  • Tooltip.Popup side prop migrated to positioner={<Tooltip.Positioner side={...} />} (@wordpress/ui 0.14 API change)
  • JSX.ElementReactElement, JSX.IntrinsicElementsReact.ComponentPropsWithRef (global JSX namespace removed in React 19)

Testing Instructions

  • npm run typecheck — passes clean
  • npm test — 1941/1941 tests pass
  • npm start — app starts locally and sites run normally

Pre-merge Checklist

  • Have you checked for TypeScript, React or other console errors?
  • Unit tests pass (npm test)

… versions

- React: 18.2.0 → 19.2.6 (react, react-dom, @types/react, @types/react-dom)
- @wordpress/components: 33.0.0 → 34.0.0
- @wordpress/compose: 7.36.0 → 8.0.0
- @wordpress/element: 6.39.0 → 7.0.0
- @wordpress/dataviews: 14.2.0 → 15.0.0
- @wordpress/icons: 13.1.0 → 13.2.0 (unpin; React 19 resolves the prior issue)
- @wordpress/i18n, @wordpress/react-i18n, and several apps/ui WP packages bumped
- @rive-app/react-canvas: 4.12.0 → 4.18.0 (first version with React 19 peer dep support)
- Root devDependencies now include react/react-dom to ensure a single hoisted copy for tests
- Rename @WordPress+components+33.0.0.patch → @WordPress+components+34.0.0.patch
- Fix React 19 TypeScript breaking changes: useRef<T>() → useRef<T>(undefined),
  ForwardedRef type, RefObject<T | null>, LegacyRef spreads in react-markdown consumers,
  allowpopups boolean attribute, Tooltip.Popup API (side → positioner prop)

Closes #3590, #3637
wojtekn and others added 6 commits May 29, 2026 17:40
@wordpress/components v34 depends on date-fns@^4.x, which conflicts with the
root date-fns@3.x required by tools/common. npm therefore keeps the package in
apps/studio/node_modules rather than hoisting it to root node_modules.

The root postinstall ran patch-package --patch-dir apps/studio/patches from
the repo root, but patch-package looks in root node_modules, so it couldn't
find @wordpress/components and failed.

Fix: move the @wordpress/components patch file to apps/studio/patches-local/
and apply it via a sub-shell cd that runs patch-package from within apps/studio,
where the package actually lives.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@wordpress/components v34 depends on date-fns@^4.x, which conflicted with the
date-fns@^3.x declared in tools/common and apps/studio, preventing npm from
hoisting the package to root node_modules. The root postinstall patch-package
call then failed because it couldn't find @wordpress/components there.

Bump date-fns from ^3.3.1 to ^4.1.0 in both tools/common and apps/studio so
root node_modules resolves date-fns v4, allowing @wordpress/components to hoist
normally. Also reverts the patches-local workaround from the previous commit.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
React 19 removes the global JSX namespace. Replace JSX.Element with
ReactElement and JSX.IntrinsicElements with React.ComponentPropsWithRef
in the affected files.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Both apps/studio and apps/ui declare react@^19.2.6, which is enough
for npm to hoist a single copy to root node_modules. The root entry
was added as a workaround for a dual-React problem that no longer
exists after fixing hoisting via the date-fns v4 upgrade.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…/ui Icon

Change Button's icon prop from ReactElement (any) to the exact type that
@wordpress/ui's Icon expects — ReactElement<ComponentProps<'svg'>> — derived
via ComponentProps<typeof Icon>['icon']. This removes the as-cast at the usage
site and makes the type mismatch visible at call sites rather than hiding it.

Update ShortcutIcon in site-shortcuts to derive from Button's icon prop type
rather than @wordpress/icons's Icon (which uses the wider ReactElement<any>),
keeping the types consistent across the chain.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- connect-button.tsx: move @wordpress/i18n import before react (import order)
- button/index.tsx: format Icon props onto one line (prettier)
- sync/index.tsx: prefix unused syncSites with _ to satisfy no-unused-vars

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@wojtekn wojtekn requested a review from a team May 29, 2026 17:32
@wpmobilebot
Copy link
Copy Markdown
Collaborator

📊 Performance Test Results

Comparing a62b505 vs trunk

app-size

Metric trunk a62b505 Diff Change
App Size (Mac) 1336.16 MB 1337.93 MB +1.77 MB 🔴 0.1%

site-editor

Metric trunk a62b505 Diff Change
load 1755 ms 1742 ms 13 ms ⚪ 0.0%

site-startup

Metric trunk a62b505 Diff Change
siteCreation 9641 ms 9615 ms 26 ms ⚪ 0.0%
siteStartup 4920 ms 4916 ms 4 ms ⚪ 0.0%

Results are median values from multiple test runs.

Legend: 🟢 Improvement (faster) | 🔴 Regression (slower) | ⚪ No change (<50ms diff)

Copy link
Copy Markdown
Member

@gcsecsey gcsecsey left a comment

Choose a reason for hiding this comment

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

LGTM, and with these changes, sites keep working normally.

@wojtekn wojtekn merged commit ec32dfa into trunk May 29, 2026
11 checks passed
@wojtekn wojtekn deleted the update-react-19-wordpress-packages branch May 29, 2026 17:59
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants