Skip to content

moebiusmania/react-webcomponents

Repository files navigation

React & WebComponents

Some examples of integrating Web Components within a React application.

Built on top of the Vite.js React + TS template.

It also uses vanilla CSS Modules to share the styles between React components and Web Components.

Introduction

As the official React documentation states, you can use Web Components within a React application.

Without additional code or techniques, Web Components are rendered inside the React Application as regular HTML elements, what is missing seamless integration between the two.

While this is being discussed (and hopefully, implemented in a near future) by the React team, as of React v16 you can manually implement the integration with a few lines of code.

Data binding

To pass simple data type from React to a Web Component you can easily use React's data binding system:

// Component.tsx
const [text, setText] = useState<string>("hey there!");

return (
  <div className="widget">
    <my-element text={text}></my-element>
  </div>
);

When it comes to object or array it will not work, a string conversion will be necessary:

// Component.tsx
const [list, setList] = useState<string[]>(["red", "green", "blue"]);

const stringed: string = JSON.stringify(list);

return (
  <div className="widget">
    <my-element list={stringed}></my-element>
  </div>
);

on the Web component side you will have to decode it back to data using JSON.parse(), but since this is a limitation of the spec itself chances are that this is already happening or that the component is authored with a framework (like Lit, Svelte, Stencil, ecc..) that handle the issue.

Events

React uses a synthetic event system while Web Components work with standard events extended with custom ones, so out-of-the-box they don't cooperate between each other.

But since React is built on top of Javascript you can use the .addEventListener() API to listen for events from DOM nodes. In order to get DOM elements without using the .querySelector() api you can use React's useRef hook to achieve the result:

// Component.tsx
const customElement = useRef<HTMLElement>(null);

const doSomething = (event: Event): void => { ... }

useEffect(() => {
  customElement?.current?.addEventListener("my-event", doSomething)

  return () => {
    customElement?.current?.removeEventListener("my-event", doSomething)
  }
}, [])

return(
  <div className="widget">
    <my-element
      ref={customElement}
    ></my-element>
  </div>
)

The most annoying part of this workaround is that you have to manually listen for events and, for performance reasons, manually remove the listener when the React's parent is being removed from the DOM.


Run locally

A few steps to quick-start the project:

Clone the repo on your machine, then

$ npm ci

to install dependencies, and

$ npm run dev

to start webserver on localhost:3000

License

Released under the MIT license.