React components are great β until you want to use them outside a React application.
Maybe you want to:
- embed a widget in a static site
- integrate React UI into a legacy app
- expose components in plain HTML
In those situations, Web Components are the universal interface.
In this article, Iβll show how to wrap a React component as a custom element that works anywhere β and how a small library makes it trivial.
π Library used in this article: Elementizer
The Problem
A React component normally requires a React environment:
import { createRoot } from "react-dom/client";
import MyComponent from "./MyComponent";
createRoot(document.getElementById("app")).render(<MyComponent />);
This means:
- React must control the rendering
- the host app must use React
- integration becomes complex
What we really want is something like this:
<my-component message="Hello world"></my-component>
Thatβs exactly what Web Components provide.
The Idea
The goal is simple:
- Take a React component
- Wrap it inside a Custom Element
- Let React render inside the element
Then the component becomes framework-agnostic.
However, it's worth mentioning that React and Web components are strongly incompatible, and every difference is subject to making things go wrong:
- their architecture is different
- the component life-cycle is different
- the state management is different
- refreshing the rendering is different
Tools like Elementizer make the process extremely lightweight.
If youβve ever wanted to ship React components that work everywhere, this approach is worth trying.
Out-of-the-box :
β
Automatic attribute conversion
β
Automatic event handling
β
Support of child nodes
β
Support of React contexts
For more complex cases :
β
Custom attribute mappers
β
Custom event mapper
β
Custom context filling
β
Custom rendering
A Simple React Component
Letβs start with a small React component.
export function Hello({ name }) {
return <h2>Hello {name} π</h2>;
}
Normally you would render it with React.
But instead, we want to expose it as:
<hello-user name="Alice"></hello-user>
Turning It into a Web Component
Using Elementizer, you can register the component as a custom element in one step.
import { createElement } from '@badcafe/elementizer';
import { Hello } from './Hello';
createElement({
name: 'hello-user',
reactComponent: Hello
});
Thatβs it.
Now the browser understands:
<hello-user name="Alice"></hello-user>
And React renders inside the element.
How it works
Unlike a React app where the React tree is superposed to the DOM tree, Elementizer manages a React tree separated from the DOM tree, which makes possible the cohabitation of React with non-React components; the link between the 2 worlds is made with portals and observers:
- The React tree is made exclusively of React portals
- React portals are rendered in the HTML tree
- Observers let React components re-render when their Web component wrapper is updated
Try It Yourself
You can explore the project and examples here: https://badcafe.github.io/elementizer/
Source code: https://github.com/badcafe/elementizer/tree/main/lib
Top comments (0)