Source:  Twitter logo

I am using web components in my application. And in a web component, I need to insert a react component. Web Component has Shadow DOM. When I try to render the react component using following I get the error.

comp = React.createElement(ReactElem, {
    config: config,
    onRender: handleRender
});

ReactDOM.render(comp , self.shadowRoot.querySelector('#app1'));

Error

target container is not a dom element

I tried to use content of Web component API but then it gets rendered on top rather inside component. Any leads how can make React component to get rendered inside shadow DOM?

If you want to insert it inside the shadow DOM of a web component, first select the component (e.g. with querySelector) and then its containing shadow (shadowRoot). Simplified example:

// Create React Element.
class Example extends React.Component {
  onClick() {
    console.log('Shadow!')
  }
  render() {
    return(
      <div onClick={this.onClick}>Hello World!</div>
    );
  }
}

// Create web component with target div inside it.
const container = document.createElement('app');
document.body.appendChild(container);

// Add shadow root to component.
const shadow = document.querySelector('app').attachShadow({ mode: 'open' });

// Select the web component, then the shadowRoot.
const target = document.querySelector('app').shadowRoot;

ReactDOM.render(<Example />, target);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
9 users liked answer #0dislike answer #09
Fabian Schultz profile pic
Fabian Schultz

Hey my friend, I took the time and crafted this for us:

// ShadowRoot.js

import React from "react";

export class ShadowRoot extends React.Component {

    attachShadow(host) {
        if (host == null) {
            return;
        }
        host.attachShadow({mode: "open"});
        host.shadowRoot.innerHTML = host.innerHTML;
        host.innerHTML = "";
    }

    render() {
        return (
            <span ref={this.attachShadow}>
                {this.props.children}
            </span>
        );
    }

}

Use it like this:

<ShadowRoot>
    // put stuff here you want inside shadow root
</ShadowRoot>

2 things to consider:

  • the class React.Component is working better than the hook equivalent
  • the innerHTML thing is kinda hacky, and state updates are not working with this component
2 users liked answer #1dislike answer #12
Hannes Schneidermayer profile pic
Hannes Schneidermayer

It looks like this answer kinda outdated, To attach a web component to a react app you need to first turn your web component into a module. You do this by pushing it to NPM with something like $) npm publish --access public

this will publish your web component as a public NPM module that can be downloaded with npm install web-components-name-here.

now your code is incorporated at build time. There is one more step you need to take and that is adding it to your root react file or inxex.js--

import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
import App from './App';
import { defineCustomElements } from 'your-custom--web-components/loader/index.js';

const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
  <React.StrictMode>
    <App />
  </React.StrictMode>
);

defineCustomElements(window)

This last bit of code is where you add's all the web components in your npm module to your Reactjs's global scope. Now you can just go ahead and use--

import React from 'react';
import './ReactFakeComp.css';
import ReactFakeComp from './App'; 

export default ReactFakeComp() {
   render(
        <your-custom--web-components />
   );
}    

anywhere in your app just like a regular HTML component.

One last thing, if you are new to building npm modules and publishing them I don't want this last part to dissuade you. I found a tool called Stencil, this is a powerful tool that allows you to build web components in an isolated env. It automatically creates all other extra data and files you will need to publish your web component either in CommonJS or newer ES^ syntax using imports.

But the most important feature is you can now build your web components in Typescript. I'm talking no more templates just straight . Stencil will convert and build everything for you.

0 users liked answer #2dislike answer #20
Justin Meskan profile pic
Justin Meskan

Copyright © 2022 QueryThreads

All content on Query Threads is licensed under the Creative Commons Attribution-ShareAlike 3.0 license (CC BY-SA 3.0).