Right way to theme in Material UI components - reactjs

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/

Related

How to add a new variant to MUI's default typography (TypeScript)

This question rose when I was trying out the MUI official instruction here.
in file newTheme.ts, customize the primary color, and add a new variant type post:
import { createTheme } from "#mui/material";
const newTheme = createTheme({
palette: {
primary: {
main: "#f00" // red
}
},
// uncommenting caused compiler error:
// Type '{ poster: { color: string; }; }' is not assignable to type 'TypographyOptions | ((palette: Palette) => TypographyOptions) | undefined'.
// Object literal may only specify known properties, and 'poster' does not exist in type 'TypographyOptions | ((palette: Palette) => TypographyOptions)'.ts(2322)
// typography: {
// poster: {
// color: '#f00',
// },
// },
});
export default newTheme;
under the same directory, added a file newTheme.d.ts:
import { ThemeOptions } from '#mui/material/styles';
declare module '#mui/material/styles' {
interface TypographyVariants {
poster: React.CSSProperties;
}
// allow configuration using `createTheme`
interface TypographyVariantsOptions {
poster?: React.CSSProperties;
}
}
// Update the Typography's variant prop options
declare module '#mui/material/Typography' {
interface TypographyPropsVariantOverrides {
poster: true;
}
}
I have two problems:
received the compiler error if I tried to add in the poster variant
the primary color didn't turn red
here is codesandbox
What had I missed?
The code in your question has the following line:
import createMuiTheme from '#material-ui/core/styles/createMuiTheme';
This is importing the v4 version of createTheme. You shouldn't be mixing any #material-ui/* packages (v4) with #mui/* packages (v5). You should be using v5 for everything.
Here's a working example using a custom Typography variant:
demo.tsx
import * as React from "react";
import { createTheme, ThemeProvider } from "#mui/material/styles";
import Typography from "#mui/material/Typography";
import Button from "#mui/material/Button";
const theme = createTheme({
typography: {
subtitle1: {
fontSize: 12
},
body1: {
fontWeight: 500
},
button: {
fontStyle: "italic"
},
poster: {
color: "red"
}
}
});
export default function TypographyVariants() {
return (
<div>
<ThemeProvider theme={theme}>
<Typography variant="subtitle1">subtitle</Typography>
<Typography variant="poster">poster</Typography>
<Typography>body1</Typography>
<Button>Button</Button>
</ThemeProvider>
</div>
);
}
typography.d.ts
import "#mui/material/styles";
import "#mui/material/Typography";
declare module "#mui/material/styles" {
interface TypographyVariants {
poster: React.CSSProperties;
}
// allow configuration using `createTheme`
interface TypographyVariantsOptions {
poster?: React.CSSProperties;
}
}
// Update the Typography's variant prop options
declare module "#mui/material/Typography" {
interface TypographyPropsVariantOverrides {
poster: true;
}
}

How can i get the context of the MUI theme?

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

react material-ui v5 theming doesnt work with storybook

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.

extending default theme chakra ui

I want to set default borderColor to all Input components but it doesn't work
This is my theme.js file:
import { extendTheme } from "#chakra-ui/react";
const config = {
initialColorMode: "light",
useSystemColorMode: false,
};
const theme = extendTheme({
config,
components: {
Input: {
borderColor: "teal",
},
},
});
export default theme;
Input is a multi-part component. You can figure out if the component you're trying to customise is single-part or multi-part by going to the docs for that component and clicking the View theme source button at the top of the page:
How to customise the theme: Docs
How to customise single-part and multi-part components: Docs (especially, how to customise multi-part components)
So in your case you need do something like this:
index.js :
import * as React from "react";
import { render } from "react-dom";
import { ChakraProvider, extendTheme } from "#chakra-ui/react";
import App from "./App";
const Input = {
variants: {
filled: () => ({
field: {
borderColor: "teal"
}
})
}
};
const theme = extendTheme({
components: {
Input
}
});
const rootElement = document.getElementById("root");
render(
<ChakraProvider theme={theme}>
<App />
</ChakraProvider>,
rootElement
);
App.js :
import * as React from "react";
import { Input } from "#chakra-ui/react";
export default function App() {
return <Input placeholder="extra small size" variant="filled" />;
}

How to get the label in the material-ui text field to the right?

I'm using material-ui and typescript for my react project (rtl layout) and i don't know how to get the label of text field to the right?
Found a better way without external libraries.
export const theme = createTheme({
components: {
MuiInputLabel: {
styleOverrides: {
root: {
left: 'inherit',
right: '1.75rem',
transformOrigin: 'right',
},
},
},
MuiOutlinedInput: {
styleOverrides: {
notchedOutline: {
textAlign: 'right',
},
},
},
},
})
You need jss-rtl to support rtl for css. This library provides its Provider to support rtl in any library.
import React from "react";
import { create } from "jss";
import rtl from "jss-rtl";
import JssProvider from "react-jss/lib/JssProvider";
import { createGenerateClassName, jssPreset } from "#material-ui/core/styles";
// Configure JSS
const jss = create({ plugins: [...jssPreset().plugins, rtl()] });
// Custom Material-UI class name generator.
const generateClassName = createGenerateClassName();
function RTL(props) {
return (
<JssProvider jss={jss} generateClassName={generateClassName}>
{props.children}
</JssProvider>
);
}
export default RTL;
Then in your main app use this provider.
ReactDOM.render(
<RTL>
<Demo />
</RTL>,
document.querySelector("#root")
);
Working demo here

Resources