Reputation: 111
i am trying to build web components from react components, it is all working fine, but there are two problems i am trying to solve:
thanks
Upvotes: 11
Views: 15869
Reputation: 2666
For react 18+ with dom unmount
import React from "react"
import ReactDOM from "react-dom/client"
export function defineWebComponent(name: string, Component: React.FC) {
customElements.define(
name,
class extends HTMLElement {
private root: ReactDOM.Root
constructor() {
super()
this.root = ReactDOM.createRoot(this)
}
connectedCallback() {
this.root.render(
<React.StrictMode>
<Component/>
</React.StrictMode>
)
}
disconnectedCallback() {
this.root.unmount()
}
})
}
usage
// main.ts
import App from './App.tsx'
defineWebComponent('app-root', App)
then compile and import script on html
<!-- index.html -->
<app-root>
</app-root>
Optional
// make typescript happy
import App from "../App.tsx";
import type {ComponentProps} from "react"
declare module "react" {
namespace JSX {
interface IntrinsicElements {
"app-root": ComponentProps<typeof App>
}
}
}
Upvotes: 1
Reputation: 71
If you're looking to do this manually, the answer from Harshal Patil is the way to go. I also wanted to point out a library that I helped create, react-to-webcomponent. It simplifies this process and seamlessly supports React 16-18.
Upvotes: 7
Reputation: 11419
React is not a library made to build native web-components.
Writing web-components by hand is not the best option neither as it won't handle conditions, loops, state change, virtual dom and other basic functionalities out of the box.
React, Vue Svelte and other custom libraries certainly have some pros while native web-components have many other advantages like simplicity of ecosystem and being ported by browsers and W3C.
Some libraries that will help you write native web-components in a modern and handy way:
If you really wanted to wrap a React component into a native web component, you could do something like:
class MyComponent extends HTMLElement {
constructor() {
this.innerHTML = '<MyReactComponent />'
}
}
customElements.define('my-component', MyComponent)
And use it in your HTML page <my-component />
Upvotes: -2
Reputation: 21030
For the first question, there is no direct way to convert React component into a Web Component. You will have to wrap it into a Web Component Class:
export function MyReactComponent() {
return (
<div>
Hello world
</div>
);
}
class MyWebComponent extends HTMLElement {
constructor() {
super();
// Do something more
}
connectedCallback() {
// Create a ShadowDOM
const root = this.attachShadow({ mode: 'open' });
// Create a mount element
const mountPoint = document.createElement('div');
root.appendChild(mountPoint);
// You can directly use shadow root as a mount point
ReactDOM.render(<MyReactComponent />, mountPoint);
}
}
customElements.define('my-web-component', MyWebComponent);
Of course, you can generalize this and create a reusable function as:
function register(MyReactComponent, name) {
const WebComponent = class extends HTMLElement {
constructor() {
super();
// Do something more
}
connectedCallback() {
// Create a ShadowDOM
const root = this.attachShadow({ mode: 'open' });
// Create a mount element
const mountPoint = document.createElement('div');
root.appendChild(mountPoint);
// You can directly use shadow root as a mount point
ReactDOM.render(<MyReactComponent />, mountPoint);
}
}
customElements.define(name, MyWebComponent);
}
register(MyReactComponent, 'my-web-component');
Same register function can be now re-used across all the components that you want to expose as web components. Further, if your component accepts props that should be passed, then this function can be changed to accept third argument as array of string where each value would be registered as a setter for this component using Object.define
. Each time a setter is called, you can simply call ReactDOM.render
again.
Now for the second question, there are multiple scenarios with what you are trying to do.
import
or require
from the global environment where app will run.Bundling for libraries is slightly trickier as you will have to decide what will be your output format (common.js, ES Modules or UMD or Global or multiple formats). The ideal format is ES Module if you are bundling for browser as it allows better tree shaking. Webpack previously did not support Module format and has recently started supporting it. In general, I recommend Webpack for applications and Rollup.js for libraries.
Upvotes: 13