Skip to content

Element: Make the package importable in React 19 - #80053

Merged
jsnajdr merged 3 commits into
WordPress:trunkfrom
SirLouen:chore/make-element-importable-react-19
Jul 30, 2026
Merged

Element: Make the package importable in React 19#80053
jsnajdr merged 3 commits into
WordPress:trunkfrom
SirLouen:chore/make-element-importable-react-19

Conversation

@SirLouen

@SirLouen SirLouen commented Jul 9, 2026

Copy link
Copy Markdown
Member

What?

Follow up to #80024

Makes @wordpress/element importable under React 19 and widens the React peer ranges of the design-system packages like ui and theme to accept React 18 or 19.

The idea is to do a smaller working iteration rather than having to wait for a fully React 19 compliant build, which may take long and doesn't seem to be landing anywhere near WP 7.1 according to #71336

Two changes in @wordpress/element:

  1. src/react-platform.ts no longer uses named imports for APIs that React 19 removed from react-dom (findDOMNode, render, hydrate, unmountComponentAtNode).
  2. react, react-dom and their types move from dependencies to peerDependencies (^18.0.0 || ^19.0.0), matching every sibling package.
  3. Plus the range widening (^18.0.0 -> ^18.0.0 || ^19.0.0) in the five design-system packages.

Why?

As a consumer running the design-system packages ui and theme in a React 19 app, the use case tracked in #76941. I find two problems:

Problem 1: @wordpress/element crashes at load time under React 19. React 19 deleted these four exports from react-dom, so the module fails before any code runs:

Problem 2: Declaring React as a hard dependency creates a second React. For some reason I don't understand, element is the only package in this family declaring react/react-dom as dependencies.

How?

Removing API from react-dom instead of named-importing them:

  1. React 18: zero behavior change, deprecated exports keep working as before
  2. React 19: names resolve as undefined. There is no scenario where this will cause regression because React 19 cannot use them by definition.

Testing Instructions

  1. Create a React 18 project consuming @wordpress/element: no change: createRoot, createPortal, flushSync work, and the deprecated render/findDOMNode still function as before.

  2. Create a React 19 project: import { createRoot } from '@wordpress/element' now loads and works

  3. 🐞 Previously: SyntaxError at import.

@SirLouen
SirLouen requested review from jsnajdr and manzoorwanijk July 9, 2026 13:29
@SirLouen
SirLouen requested review from a team and ajitbohra as code owners July 9, 2026 13:29
@SirLouen SirLouen added [Type] Enhancement A suggestion for improvement. [Package] Element /packages/element labels Jul 9, 2026
@github-actions github-actions Bot added [Package] Compose /packages/compose [Package] Primitives /packages/primitives [Package] Icons /packages/icons [Package] Theme /packages/theme [Package] UI /packages/ui labels Jul 9, 2026
@SirLouen SirLouen changed the title Element: Make the package importable in React 19 Element: Make the package and siblings importable in React 19 Jul 9, 2026
@simison

simison commented Jul 9, 2026

Copy link
Copy Markdown
Member

Somewhat duplicate of #80024 from @aduth ?

@github-actions

github-actions Bot commented Jul 9, 2026

Copy link
Copy Markdown

The following accounts have interacted with this PR and/or linked issues. I will continue to update these lists as activity occurs. You can also manually ask me to refresh this list by adding the props-bot label.

If you're merging code through a pull request on GitHub, copy and paste the following into the bottom of the merge commit message.

Co-authored-by: SirLouen <sirlouen@git.wordpress.org>
Co-authored-by: manzoorwanijk <manzoorwanijk@git.wordpress.org>
Co-authored-by: jsnajdr <jsnajdr@git.wordpress.org>
Co-authored-by: simison <simison@git.wordpress.org>
Co-authored-by: aduth <aduth@git.wordpress.org>

To understand the WordPress project's expectations around crediting contributors, please review the Contributor Attribution page in the Core Handbook.

@SirLouen

SirLouen commented Jul 9, 2026

Copy link
Copy Markdown
Member Author

Somewhat duplicate of #80024 from @aduth ?

Good spotting, it's partially duplicate, I was hesitant on sending both versions in one commit, but yes, I'm going to remove the duplicate parts right now and just leave the compatibility layer for element that was my original intention given that #80024 fixes the most compatibility part of this patch.

Updated 👌
Probably it will need a rebase once #80024 is merged

@SirLouen
SirLouen force-pushed the chore/make-element-importable-react-19 branch from c1df306 to 48c6bd0 Compare July 9, 2026 13:57
@github-actions github-actions Bot removed [Package] Compose /packages/compose [Package] Primitives /packages/primitives [Package] Icons /packages/icons labels Jul 9, 2026
@SirLouen SirLouen changed the title Element: Make the package and siblings importable in React 19 Element: Make the package importable in React 19 Jul 9, 2026
@github-actions github-actions Bot removed [Package] Theme /packages/theme [Package] UI /packages/ui labels Jul 9, 2026
@SirLouen
SirLouen requested review from aduth and simison July 9, 2026 14:00
@SirLouen
SirLouen force-pushed the chore/make-element-importable-react-19 branch from 48c6bd0 to 32caa19 Compare July 9, 2026 18:22
@SirLouen

SirLouen commented Jul 9, 2026

Copy link
Copy Markdown
Member Author

Fresh rebase with 28e3787
Ready for review :)

@manzoorwanijk manzoorwanijk left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I think we need a changelog entry, making this one a breaking change.

Comment thread packages/element/package.json Outdated
Comment thread packages/element/package.json
@SirLouen
SirLouen force-pushed the chore/make-element-importable-react-19 branch from 32caa19 to 0ec12d1 Compare July 10, 2026 20:26
@SirLouen

Copy link
Copy Markdown
Member Author

I think we need a changelog entry, making this one a breaking change.

True, added the CHANGELOG.md entry. It cannot be left as a dependency; that wasn't the right call originally.

@SirLouen

Copy link
Copy Markdown
Member Author

@aduth do you have any roadmap in mind to work around this?

@aduth

aduth commented Jul 29, 2026

Copy link
Copy Markdown
Member

I think we definitely need to fix this. Some of the other work around this (like #80706) only really indirectly solve some of the symptoms, but the impact of @wordpress/element and its compatibility in projects that use React 19 is widespread. I've been hearing similar pain from some other folks at my company about this as well.

I think it'd be good to have a review here from @jsnajdr , who has been leading a lot of the work around the React 19 upgrade.

/* eslint-enable react/no-deprecated */
} from 'react-dom';
import { createRoot, hydrateRoot } from 'react-dom/client';
} = ReactDOM;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Why do we need to reorganize this import?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

It's not a cosmetic reorganization.

React 19 removed findDOMNode, render, hydrate, and unmountComponentAtNode from react-dom.

With static named imports, those bindings must be resolvable at module link time. When a consumer loads @wordpress/element in a R19 app, the ESM loader tries to bind names that no longer exist on react-dom and throws for example:

SyntaxError: The requested module 'react-dom' does not provide an export named 'render'

Solution: A namespace import like this.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Thanks, that makes sense. import { render } throws, but accessing a property of the namespace object (ReactDOM.render) just returns undefined.

Which ESM runtime does throw exactly this error? The native ESM resolver in Node, or in browsers? Or a bundler (esbuild or webpack) runtime?

SyntaxError: The requested module 'react-dom' does not provide an export named 'render'

Comment thread packages/element/package.json Outdated
@SirLouen
SirLouen force-pushed the chore/make-element-importable-react-19 branch from 3f416ac to 9ff36a0 Compare July 29, 2026 19:32
@SirLouen
SirLouen requested a review from jsnajdr July 29, 2026 19:32

@jsnajdr jsnajdr left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Thanks, now the element React dependencies are aligned with how other packages declare them and don't do anything special. And are compatible with React 19. Nice.

@jsnajdr
jsnajdr merged commit 190111f into WordPress:trunk Jul 30, 2026
44 of 45 checks passed
@github-actions github-actions Bot added this to the Gutenberg 23.8 milestone Jul 30, 2026
@SirLouen
SirLouen deleted the chore/make-element-importable-react-19 branch July 30, 2026 13:17
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

[Package] Element /packages/element [Type] Enhancement A suggestion for improvement.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants