mui / material-ui

Material UI: Comprehensive React component library that implements Google's Material Design. Free forever.
https://mui.com/material-ui/
MIT License
93.86k stars 32.26k forks source link

Styles not added to shadow DOM when wrapping MUI in a library #42992

Open MartinJaskulla opened 3 months ago

MartinJaskulla commented 3 months ago

Steps to reproduce

Link to live example: https://6698f74d46bade9e76f1d058--guileless-sorbet-bf347d.netlify.app/

https://github.com/MartinJaskulla/reproduction-library https://github.com/MartinJaskulla/reproduction-library-user

  1. git clone https://github.com/MartinJaskulla/reproduction-library-user
  2. npm i
  3. npm run dev
  4. See the bug: MUI <Button> is unstyled, because emotion style sheets are added to <head> instead of shadow DOM
  5. Comment out Button, and comment in // import {Button} from "@mui/material"; in main.jsx
  6. Bug gone

Current behavior

<Button> looks like a native html <button> inside shadow DOM.

Expected behavior

<Button> should look like a MUI button inside shadow DOM.

Context

I created a component library for my company that wraps MUI and makes some adjustments. Everything works flawlessly.

For a new feature we have to use my library inside shadow DOM meaning the project that is installing my component library is creating a custom element and wants to use the wrapped MUI components from my library inside it. I followed the documentation (https://mui.com/material-ui/customization/shadow-dom/) to do that. Somehow the styles do get added to the <head> instead of the shadow DOM.

I created a new library for my reproduction, in which I just re-export MUI as is (that is the only thing the library is doing): https://github.com/MartinJaskulla/reproduction-library/blob/main/src/main.jsx

export * from "@mui/material";

If I install this library in a new project (https://github.com/MartinJaskulla/reproduction-library-user), then import { Button } from "@mui/material"; adds the styles correctly to the shadow DOM, but import { Button } from "reproduction-library"; adds the styles to the <head> although the code of both buttons should be identical.

To me it looks like MUI only works inside shadow DOM if imported from "@mui/material";.

Your environment

npx @mui/envinfo ``` System: OS: macOS 14.5 Binaries: Node: 22.2.0 - /opt/homebrew/bin/node npm: 10.7.0 - /opt/homebrew/bin/npm pnpm: Not Found Browsers: Chrome: 126.0.6478.182 Edge: Not Found Safari: 17.5 ```

Search keywords: shadowdom library styles emotion

MartinJaskullaTS commented 3 months ago

What I figured out so far:

There are two relevant lines in node_modules/@emotion/sheet/dist/emotion-sheet.browser.esm.js.

The first relevant line sets the container the <style> elements will get added to later. options.container is the shadowContainer I passed to createCache:

this.container = options.container;

The second relevant line adds the <style> element to the container:

_this.container.insertBefore(tag, before)

If I import Button from "@mui/material", then both lines are executed from node_modules/@emotion/sheet/dist/emotion-sheet.browser.esm.js.

If I import from Button from "reproduction-library", then the first line is executed from node_modules/@emotion/sheet/dist/emotion-sheet.browser.esm.js, but the second line is executed from my library bundle at node_modules/reproduction-library/dist/main.js.


How I debugged this:

MartinJaskullaTS commented 3 months ago

If I bundle CacheProvider in my library and then import it from "reproduction-library" instead of "@emotion/react", the problem disappears.

I don't know if this is the recommended approach though. Maybe this should be documented somewhere? As others have stated before, it would be nice to have docs/an example/a template about how to wrap MUI to build your own component library.


Maybe this issue can be closed.


For reference:

In the library:

export * from "@mui/material";
export {CacheProvider} from "@emotion/react"`