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.
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.
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.
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.
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
Released under the MIT license.