'How to create a shared component library with MUI 5
I have a custom MUI 5 component library NPM package that exports some basic components built with MUI 5, exports theme file and wraps the theme provider into reusable component like so:
import { ThemeProvider } from 'styled-components';
import { StyledEngineProvider } from '@mui/material/styles';
import { theme as defaultTheme } from './theme';
function CustomThemeProvider(props: {
theme: any;
children: JSX.Element[];
}): JSX.Element {
const { theme = defaultTheme, children } = props;
return (
<StyledEngineProvider injectFirst>
<ThemeProvider theme={theme}>{children}</ThemeProvider>
</StyledEngineProvider>
);
}
export default CustomThemeProvider;
However when I try using this custom theme provider and the components in the project that uses this shared library, theme is not applied at all. This approach did work with MUI 4, but there was no Emotion layer at that point and setup was much simpler.
I have added all of these in peer dependencies for the shared component package to keep the references intact.
"peerDependencies": {
"@emotion/react": "^11.7.1",
"@emotion/styled": "^11.6.0",
"@mui/icons-material": "^5.2.4",
"@mui/lab": "^5.0.0-alpha.60",
"@mui/styles": "^5.2.3",
"@mui/material": "^5.2.4",
"@mui/styled-engine-sc": "^5.1.0",
"react": "^17.0.2",
"react-dom": "^17.0.2",
"styled-components": "^5.3.3"
},
But it does not make any difference.
Any ideas how to make it work, or any other approach that allows for a shared MUI 5 component library / theme to work, are welcome!
Solution 1:[1]
Not sure if this is the same situation, but in my case this was a problem of using npm link
for local development.
NOTE: You could also run into this problem if @mui
is not listed as a peerDependency
of your component package.
TLDR: If your shared component library package is installed with its own copy of @mui
(i.e. /node_modules/pkg/node_modules/@mui
) then your shared ThemeProvider won’t work.
I have a very similar setup, with a shared npm package (@my/custom-components
) with MUI as a peer dependency. In the consuming app, I used npm link @my/custom-components
.
The problem with using link is it creates a symlink to your local package, which isn’t representative of the final build. When @my/custom-components
is published, it only includes the package.json
and a dist/
folder. But on my local machine, it includes all the source files, including the node_modules
folder.
Normally, a package with peerDependencies won’t have its own sub node_modules
folder, to ensure it uses the host app’s dependencies. But with this local setup, @my/custom-components
was configuring its own copy of MUI, instead of the host app’s MUI:
// When using npm link
host-app
??? node_modules
? ??? @my/custom-components
? ? ??? dist
? ? ??? node_modules
? ? ? ??? @mui/material-ui <== conflicting copy
? ? ??? package.json
? ??? @mui/material-ui <== host copy
??? src
??? package.json
// When installed normally
host-app
??? node_modules
? ??? @my/custom-components
? ? ??? dist
? ? ??? package.json
? ??? @mui/material-ui <== only copy
??? src
??? package.json
To fix the problem, I:
- Removed the sym link (
npm install
in the host app) - Manually built
@my/custom-components
(npm pack
) - Replaced
host-app/node_modules/@my/custom-components
with the built package.
Solution 2:[2]
I found a solution, what i did was,
create the theme project with custom build configurations with rollup. at their I added @mui/ as external packages. then export the mui5 theme provider.
then consumes it in the consumer project. consumer project need to have mui5 as dependencies. simple we are sharing the mui5.
sample rollup config ==>
import resolve from '@rollup/plugin-node-resolve';
import commonjs from '@rollup/plugin-commonjs'; import typescript from 'rollup-plugin-typescript2';
const packageJson = require('./package.json'); // eslint-disable-line @typescript-eslint/no-var-requires
export default {
input: 'src/index.ts',
external: [
'react',
'react-dom',
'prop-types',
'@emotion/react',
'@emotion/styled',
'@mui/lab',
'@mui/icons-material',
'@mui/material',
'@mui/material/styles',
'@mui/styles',
],
output: [
{
file: packageJson.main,
format: 'cjs',
sourcemap: true,
},
{
file: packageJson.module,
format: 'esm',
sourcemap: true,
},
],
plugins: [
resolve(),
commonjs(),
typescript({ useTsconfigDeclarationDir: true }),
],
};
Solution 3:[3]
I have found a solution. Adding external theme provider was an overkill that I converted from MUI4.
In MUI 5 only thing that is needed is shared theme file. So place the theme file in the shared component library and import it in the projects that use it, but keep the theme providers local in all three codebases.
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|---|
Solution 1 | |
Solution 2 | Vimukthi Jayasinghe |
Solution 3 | Dave Anders |