When to reach for Web Components vs React in a WordPress build
They solve overlapping problems. Here is the decision tree I use on client work, and where each one quietly falls down.
Client calls start with a reasonable ask: we need something interactive on WordPress, and we want it to feel modern. Then the constraints arrive. The theme is old, the budget is fixed, the in-house developer knows PHP, the agency before you left a React admin app in a plugin, and someone on the board read an article about Web Components being the future.
React and Web Components overlap in what they can deliver but not in how they fit WordPress politics, theme architecture, or the next handoff. I use a short decision tree on every project so I am not picking based on whatever I used last week.
First question: who owns the DOM you mount into?
If you control the theme template, the enqueue strategy, and the build pipeline, React is usually the straight path. You mount roots where you need them, share components between the block editor and the front end if that is in scope, and lean on patterns WordPress already documents for @wordpress/element and modern plugins.
If you are dropping a widget into a theme someone else maintains, or into a page builder layout you cannot predict, Web Components earn their keep. The integration surface is HTML. <donation-meter goal="50000"></donation-meter> survives a handoff better than “call window.initDonationMeter('#sidebar > div:nth-child(2)') after the carousel script runs.”
When ownership is mixed, hybrid is normal. React inside Gutenberg for editorial components, Web Components on the public site for the same feature’s front-end shell, shared REST endpoints underneath both.
Second question: does the UI need to live inside Gutenberg?
Gutenberg is React. Fighting that with Web Components in the block editor is possible but awkward. You wrap custom elements in a block shell, deal with editor preview styling, and explain to editors why the block sidebar behaves differently from native blocks. For editor experiences, React with @wordpress/block-editor primitives is the default unless the UI is truly trivial.
Web Components shine on the front end and in static theme templates, not as a replacement for block editor ergonomics. If the feature is editor-only, React. If the feature is visitor-facing inside a legacy theme, lean Web Components.
Third question: how bad is the global CSS situation?
Legacy themes weaponize global selectors. One rogue !important rule can ruin a React widget styled with CSS modules if anything leaks outside your root. Shadow DOM on Web Components isolates styles in a way that saves weeks on some advocacy and nonprofit sites I have worked on.
React can use CSS modules, shadow roots via libraries, or scoped styling, but the default React path does not give you encapsulation for free. If the theme is chaotic and you cannot get a rewrite, Web Components reduce style collision risk. If the theme is modern block theme with sensible tokens, React styling is fine.
Fourth question: what does your team maintain after launch?
A PHP-heavy client team can paste a custom element into a template and update attributes. They may not want a Node build step for every text change. Web Components still need a build for complex logic, but the HTML contract stays readable.
A team already running @wordpress/scripts, esbuild, and CI for blocks will move faster in React because hiring, docs, and debugging stack traces all align with the WordPress JavaScript ecosystem. Pick the tool that matches the people who will answer support email in year two.
Fifth question: how large is the interactive surface?
Small behaviors (toggle, tabs, simple filter) might not need either framework. Vanilla JS or Alpine-style sprinkles can be enough. Medium widgets (search, multi-step forms, calculators) fit Web Components well when integration matters more than shared state across dozens of screens. Large admin workflows and editor tooling almost always land in React with @wordpress/data.
If you are building something that would be a multi-route SPA in any other stack, React inside wp-admin or a headless-adjacent theme setup is the less surprising choice.
Where each option falls down
React falls down when you bundle duplicate copies, mount into unstable DOM nodes, or spread ten unrelated roots across a theme with no shared data story. It also falls down politically when the next vendor does not want Node in the workflow even though the site already runs React in five plugins.
Web Components fall down when you need rich editor integration, when you skip progressive enhancement, when you assume Shadow DOM fixes theme scripts deleting your host node, or when you over-custom-element everything and debugging becomes a forest of shadow roots.
A practical decision tree
Need deep Gutenberg integration?
yes -> React
no -> continue
Mounting into legacy theme or unknown DOM?
yes -> Web Components (or hybrid)
no -> continue
Team lives in WordPress JS toolchain?
yes -> React
no -> Web Components with clear HTML API
Global CSS chaos?
yes -> Web Components with Shadow DOM
no -> either works; prefer team familiarity
Tiny interaction?
-> vanilla JS first
Hybrid patterns that actually ship
I have shipped React blocks that render server-side markup and hydrate a Web Component on the front end for the interactive layer. The block editor preview uses React; visitors get an encapsulated element. REST endpoints serve both. The duplication is intentional and documented.
I have also shipped Web Components that wrap an internal React tree, exposing only attributes and events externally. The theme never imports React, but developers still use familiar tools inside the shell.
The point is constraints, not religion
React and Web Components are not rivals in abstract. They are tools that fit different WordPress constraints. Choose based on who owns the theme, where the UI lives, how toxic the global CSS is, and who maintains the thing after you leave. Document the boundary, keep REST and auth in PHP where they belong, and ship something the client can still edit when your contract ends.
That decision tree takes ten minutes in a kickoff call and saves months of the wrong abstraction fighting the wrong theme.