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);