I am attempting to pass a theme by doing:
declare module "#mui/styles/defaultTheme" {
// eslint-disable-next-line #typescript-eslint/no-empty-interface
interface DefaultTheme extends Theme {}
}
ReactDOM.render(
<StyledEngineProvider injectFirst>
<ThemeProvider theme={theme}>
<Main />
</ThemeProvider>
</StyledEngineProvider>,
document.getElementById("root")
);
which I them attempt to read by doing:
import { styled } from "#mui/system";
type StyledTabProps = {
isActive: boolean;
};
const StyledTab = styled("div", {
shouldForwardProp: (prop) => prop !== "isActive"
})<StyledTabProps>(({ theme, isActive }) => {
console.log("theme", theme);
return {
color: isActive ? "red" : "blue"
};
});
the theme which I attempt to pass is different from the one that ends up getting console.logged (is missing properties in palette object)
code sandbox of the issue can be found here:
https://codesandbox.io/s/wandering-https-ubhqs?file=/src/Main.tsx
You just need to use ThemeProvider from #mui/material/styles, not from #mui/styles. Then it would work.
Please refer to this
// index.tsx
...
import { Theme, ThemeProvider, StyledEngineProvider } from "#mui/material/styles";
...
And also MUI v5 theme structure is a bit different not exactly the same to the v4.
You could not use adaptV4Theme, just update the theme structure, but please define some custom types for it.
(The codesandbox says adaptV4Theme is deprecated somehow.)
For example, you used overrides option for the theme object, but it is removed so you need to use components instead of it. https://mui.com/guides/migration-v4/#theme-structure
...
// styles/theme.ts
export default createTheme(
{
...
components: {
MuiInputBase: {
root: {
...
}
...
Related
This is how you create a theme and propagate it using MUI.
import { ThemeProvider } from "#mui/material";
const myTheme = createTheme({
backgroundColor: {
primary: "#f9f9fb",
secondary: "#ededf3",
tertiary: "#cbcbdc",
},
})
const Index: FC = () => {
<ThemeProvider theme={theme}>
<App />
</ThemeProvider>
};
Now, for some reason, I need to get the context of the MUI theme in my app.
I've search everywhere but it seems that they do not expose the context anywhere. I found 3 contexts in private-theming, styled-engine and styled-engine-sc but none of them worked.
How can I do that ?
The way you create the theme it wrong should be like following:
const theme = createTheme({
palette: {
primary: {
main: red[500],
},
},
});
with the property palette.
and the way to get values you could use the hook useTheme .
first import
import { useTheme } from "#mui/material";
and inside your components you can use the palette you set to your theme like:
const { palette } = useTheme();
MUI uses the context provided by the styled engine:
import { ThemeContext } from '#mui/styled-engine';
It can also be imported directly from your engine of choice (emotion in the example below):
import { ThemeContext } from '#emotion/react';
From the Material UI website, there seem to be multiple ways of theming, and I'm not sure which to use.
There's this method, which just seems to use pure React context. However, I don't see how main relates to the color. I'm not sure how it is a field that Checkbox is able to extract from, given we don't specify it in the HTML.
import * as React from 'react';
import { createTheme, ThemeProvider } from '#mui/material/styles';
import Checkbox from '#mui/material/Checkbox';
import { green, orange } from '#mui/material/colors';
const outerTheme = createTheme({
palette: {
primary: {
main: orange[500],
},
},
});
const innerTheme = createTheme({
palette: {
primary: {
main: green[500],
},
},
});
export default function ThemeNesting() {
return (
<ThemeProvider theme={outerTheme}>
<Checkbox defaultChecked />
<ThemeProvider theme={innerTheme}>
<Checkbox defaultChecked />
</ThemeProvider>
</ThemeProvider>
);
}
There's this method, which seems to make more sense. Here we explicitly extract the theme color via theme.status.danger and place it into the color field.
import * as React from 'react';
import Checkbox from '#mui/material/Checkbox';
import { createTheme, ThemeProvider, styled } from '#mui/material/styles';
import { orange } from '#mui/material/colors';
declare module '#mui/material/styles' {
interface Theme {
status: {
danger: string;
};
}
// allow configuration using `createTheme`
interface ThemeOptions {
status?: {
danger?: string;
};
}
}
const CustomCheckbox = styled(Checkbox)(({ theme }) => ({
color: theme.status.danger,
'&.Mui-checked': {
color: theme.status.danger,
},
}));
const theme = createTheme({
status: {
danger: orange[500],
},
});
export default function CustomStyles() {
return (
<ThemeProvider theme={theme}>
<CustomCheckbox defaultChecked />
</ThemeProvider>
);
}
I could also change the MUI nested component:
import * as React from 'react';
import { ThemeProvider, createTheme } from '#mui/material/styles';
import Button from '#mui/material/Button';
const theme = createTheme({
components: {
MuiButton: {
styleOverrides: {
root: {
fontSize: '1rem',
},
},
},
},
});
export default function GlobalThemeOverride() {
return (
<ThemeProvider theme={theme}>
<Button>font-size: 1rem</Button>
</ThemeProvider>
);
}
Which is the right way to theme?
Good evening, I've recently started using UI stuff and from what I've seen in version 5, the theme as you mentioned is used. Another way I'm styling it is using the SX props on the element.
https://mui.com/pt/system/the-sx-prop/
I'm currently working in a design system library to test some things.
Basically, the library is a Styled Component wrapper in order to create themes.
I built this library with Typescript, React, Styled Components and Rollup as bundler.
The problem is when the user use this library, the vscode doesn't helps with the typechecking for the theme declarations.
This is the index.js from the library. Basically is a HOC in which we have a context and a ThemeProvider HOC from Styled components.
import { createContext, useState } from "react";
import "styled-components";
import {
DefaultTheme,
ThemeProvider as SCThemeProvider,
} from "styled-components";
import { themes } from "./design-system/colorTheme";
import React from "react";
interface ThemeContextAPI {
toggleTheme: () => void;
currentTheme: DefaultTheme;
}
const ThemeContext = createContext<ThemeContextAPI | null>(null);
const ThemeProvider: React.FC<{ children?: React.ReactNode }> = ({
children,
}) => {
const [currentTheme, setCurrentTheme] = useState<DefaultTheme>(themes.light);
const toggleTheme = () => {
setCurrentTheme((prev) =>
prev === themes.light ? themes.dark : themes.light
);
};
const values: ThemeContextAPI = {
toggleTheme,
currentTheme,
};
return (
<ThemeContext.Provider value={values}>
<SCThemeProvider theme={currentTheme}>{children}</SCThemeProvider>
</ThemeContext.Provider>
);
};
export default ThemeProvider;
export { ThemeContext };
As we see, we have a SCThemeProvider that is a ThemeProvider component from Styled component. This brings to the user, use all the theme variables inside all the styled components. The theme that this component provides is extended as the SC documentation says, so DefaultTheme is declared like this:
export interface Theme {
primary?: Color;
primaryContainer?: Color;
secondary?: Color;
secondaryContainer?: Color;
tertiary?: Color;
tertiaryContainer?: Color;
cuaternary?: Color;
cuaternaryContainer?: Color;
// -------------
surface?: Color;
surfaceVariant?: Color;
background?: Color;
error?: Color;
errorContainer?: Color;
// -------------
onPrimary?: Color;
onPrimaryContainer?: Color;
onSecondary?: Color;
onSecondaryContainer?: Color;
onTertiary?: Color;
onTertiaryContainer?: Color;
onCuaternary?: Color;
onCuaternaryContainer?: Color;
// --------------
onSurface?: Color;
onSurfaceVariant?: Color;
onError?: Color;
onErrorContainer?: Color;
onBackground?: Color;
outline?: Color;
// --------------
inverseSurface?: Color;
inverseOnSurface?: Color;
inversePrimary?: Color;
colorPalette: ColorPalette<ColorSet>;
surfaceTones: SurfaceTones;
}
declare module "styled-components" {
export interface DefaultTheme extends Theme {}
}
The library is consumed like this, (The example is written in next.js):
import { GlobalStyle, ThemeProvider } from "rakes-design-sys";
function MyApp({ Component, pageProps }) {
return (
<>
<ThemeProvider>
<GlobalStyle />
<Component {...pageProps} />
</ThemeProvider>
</>
);
}
export default MyApp;
But when I use the theme inside a styled component, the vscode doesn´t shows the types of the theme. I´ve already configured TS config to create declarations files but still doesn't work. I just put this in tsconfig and rollupconfig
"declaration": true,
"declarationDir": "types",
"emitDeclarationOnly": true
And I use the rollup-plugin-dts like this in my rollup configuration
{
input: "dist/types/index.d.ts",
output: [{ file: "dist/index.d.ts", format: "es" }],
plugins: [dts()],
},
This generates a folder inside dist called types in which is contained all my files in the library but with the .d.ts extensions and creates another index.d.ts inside dist.
By this way typechecking works when I use the context like:
import { ThemeContext } from "rakes-design-sys";
import { useContext, useEffect } from "react";
import { OHeader } from "../styles/organisms/OHeader.styles";
const App = () => {
const { toggleTheme, currentTheme } = useContext(ThemeContext);
console.log(currentTheme.primaryContainer);
return <OHeader onClick={() => toggleTheme()}>HelloWorld</OHeader>;
};
export default App;
In the console.log(currentTheme.something) the typechecking is working but this doesn´t work inside a styledComponent like this
import styled from "styled-components";
export const OHeader = styled.div`
background-color: ${(props) => props.theme.primary};
`;
It's already working, but when I write props.theme.something, it doesn't predict or typechecking the values inside the current theme that the styled components is providing.
The provider context for styled-components acts as a singleton. So, when you import from styled-components in your library and in your consumer app, the theme context doesn't match.
To fix make styled-components a peer dependency in your library.
I spend a few days trying to customize the primary color and add two more colors to the palette. I was able to declare properly the new colors...but at the moment to see those new colors reflected on the button doesnt work. The button are taking the default properties even when I wrapped under the Themeprovider. I'm using storybook.
import React from "react";
import { Meta } from "#storybook/react/types-6-0";
import { Button } from "#mui/material";
import { createTheme, ThemeProvider, styled } from '#mui/material/styles';
const theme = createTheme({
palette: {
primary: {
contrastText: "#ff0000",
light: "#ff0000",
main: "#ff0000",
dark: "#ff0000"
},
tertiary: {
main: "#ff0000"
},
failure: {
main: "#ff0000"
}
}
});
const CustomStyles = () => {
return (
<ThemeProvider theme={theme}>
<Button variant="contained">Click me</Button>
</ThemeProvider>
);
}
const Template = () => {
return (
<CustomStyles />
);
};
export const Default = Template.bind({});
export default {
title: "mylib/Theme"
} as Meta;
This is how it looks
default button style
Themeprovider custom palette
As you may see, the ThemeProvider has the palette color definition...but some how the button doesnt take it.
Thanks in advance
Adding this to.storybook/preview.js was enough to solve my case. Follow the official migration guide on this matter to learn more.
//.storybook/preview.js
import { ThemeProvider } from '#mui/material/styles';
import { ThemeProvider as Emotion10ThemeProvider } from 'emotion-theming';
import { theme } from '../your/system/customTheme/path';
const defaultTheme = theme;
const withThemeProvider = (Story, context) => {
return (
<Emotion10ThemeProvider theme={defaultTheme}>
<ThemeProvider theme={defaultTheme}>
<Story {...context} />
</ThemeProvider>
</Emotion10ThemeProvider>
);
};
export const decorators = [withThemeProvider];
//another storybook exports.
EDIT: this issue seems to be related to stackoverflow.com/a/70254078/17724218 as OP commented below.
I want to use a color from my material-ui theme inside a component like that :
const MyComponent = props => (
<UsersIcon color={currentTheme.primary1Color} />
)
So, my need is to extract a value from the current provided theme.
I found a working solution to solve this case, using context to retrieve the current theme :
const MyComponent = (props, {muiTheme}) => (
<UsersIcon color={muiTheme.palette.primary1Color} />
)
contextTypes = {
muiTheme: PropTypes.object.isRequired,
}
The React context is used "under the hood" by material-ui, so my solution is not future proof – the implementation of MUI can change –, is there any way to solve this in a proper (or recommended) way ?
You can access the theme variables with react hook or with higher-order component.
Example with hook:
//...
import { useTheme } from '#material-ui/core/styles';
const MyComponent = () => {
const theme = useTheme();
return <UsersIcon color={theme.palette.primary.main} />
}
Example with HOC:
//...
import { withTheme } from '#material-ui/core/styles';
const MyComponent = ({theme, ...other}) => {
return <UsersIcon color={theme.palette.primary.main} />
}
export default withTheme(MyComponent)
Don't forget to wrap root application component with ThemeProvider
Another method to mention is makeStyles for CSS-in-JS styling:
//...
import { makeStyles } from '#material-ui/core/styles'
const useStyles = makeStyles(theme => ({
icon: {
color: theme.palette.primary.main
}
}))
const MyComponent = () => {
const classes = useStyles()
return <UsersIcon className={classes.icon} />
}
Yes you have! using muiThemeable..
import muiThemeable from 'material-ui/styles/muiThemeable';
const MyComponent = props => (
<UsersIcon color={props.muiTheme.palette.primary1Color} />
)
export default muiThemeable()(MyComponent )
from material-ui docs
If your colors don't change at runtime, you can store these constants in a global object that gets used to initialize the theme as well as used in your custom components. This would allow you to not depend on context while keeping your code dry.