DEV Community

Cover image for Turn a React Component into a Web Component (and Use It Anywhere)
Philippe Poulard
Philippe Poulard

Posted on

Turn a React Component into a Web Component (and Use It Anywhere)

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 />);
Enter fullscreen mode Exit fullscreen mode

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>
Enter fullscreen mode Exit fullscreen mode

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>;
}
Enter fullscreen mode Exit fullscreen mode

Normally you would render it with React.

But instead, we want to expose it as:

<hello-user name="Alice"></hello-user>
Enter fullscreen mode Exit fullscreen mode

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
});
Enter fullscreen mode Exit fullscreen mode

That’s it.

Now the browser understands:

<hello-user name="Alice"></hello-user>
Enter fullscreen mode Exit fullscreen mode

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)