React component's Material UI theme not scoped locally to Shadow DOM React component's Material UI theme not scoped locally to Shadow DOM reactjs reactjs

React component's Material UI theme not scoped locally to Shadow DOM


I had the same issue packaging my web application for integration with another framework, which required my React app to be exported as a Web Component. After struggling for a while with libraries like react-jss and having no luck, I finally came across this Stack Overflow answer by @shawn-mclean this morning. I had seen your question while banging my head against my desk and wanted to pass on what worked for me.

In summary, I first had to create the Web Component

WCRoot.js

import React from 'react';import ReactDOM from 'react-dom';import { StylesProvider, jssPreset } from '@material-ui/styles';import { create } from 'jss';import App from './App';class WCRoot extends HTMLElement {    connectedCallback() {        const shadowRoot = this.attachShadow({ mode: 'open' });        const mountPoint = document.createElement('span');        // first we need to store a reference to an element INSIDE of the shadow root                const reactRoot = shadowRoot.appendChild(mountPoint);        // now we use that saved reference to create a JSS configuration        const jss = create({            ...jssPreset(),            insertionPoint: reactRoot        });        // finally we need to wrap our application in a StylesProvider        ReactDOM.render(            <StylesProvider jss={jss}>                <App />            </StylesProvider>,            mountPoint);    }}customElements.define('wc-root', WCRoot);

Next, I set my index.js to render <wc-component></wc-component> instead of <App />:

index.js

import React from 'react';import ReactDOM from 'react-dom';import './index.css';// eslint-disable-next-lineimport WCRoot from './WCRoot';ReactDOM.render(<wc-root></wc-root>, document.getElementById('root'));

One thing which surprised me is that we don't need to modify the MuiThemeProvider at all...it just grabs the existing JSS context provided by the StylesProvider. I believe you should be able to modify content.js to look something like the following:

content.js

var app = $('<div id="Extension" style="position: fixed; display: block; bottom: 0px; left: 0px; width: 100vw; height: 48px; background: grey; z-index: 99999"></div>')app.prependTo('body');var shadowRoot = this.attachShadow({ mode: 'open' });var mountPoint = document.getElementById('Extension');var reactRoot = shadowRoot.appendChild(mountPoint);// see code blocks above for the import statements for `create` and `jssPreset`var jss = create({    ...jssPreset(),    insertionPoint: reactRoot});render(    <StylesProvider jss={jss}>        <App />    </StylesProvider>,    mountPoint);