How to access MUI 5 theme variables in deep functional component? - reactjs

The MUI 5 docs on Theming have a section on "Accessing the theme in a component". However, it's really just one sentence that links to the legacy style docs.
Here's the example they give in those legacy docs:
import { useTheme } from '#mui/styles';
function DeepChild() {
const theme = useTheme();
return <span>{`spacing ${theme.spacing}`}</span>;
}
Which is pretty much exactly what I want to do — I want to be able to access the theme color palette down in some deep functional component. However, my component complains
Module not found: Error: Can't resolve '#mui/styles' in...
Digging a little further, it seems they're rather strongly trying to discourage people from using this legacy Styles technique, and the MUI 5 way to do this is with "system design tokens", which I guess should Just Work. But, they're not.
I have my whole app wrapped in ThemeProvider:
import React from 'react';
import { CssBaseline } from '#mui/material';
import { ThemeProvider } from '#mui/material/styles';
import theme from './theme';
import Foo from './foo';
const App = () => {
return (
<Fragment>
<ThemeProvider theme={theme}>
<CssBaseline enableColorScheme />
<Foo />
</ThemeProvider>
</Fragment>
);
};
export default App;
And then in foo.js:
import React from 'react';
import { Box } from '#mui/material';
export const Foo = () => {
return (
<Box
sx={{
background: 'repeating-linear-gradient(-45deg, '
+ ' theme.palette.error.light, theme.palette.error.light 25px,'
+ ' theme.palette.error.dark 25px, theme.palette.error.dark 50px'
+ ')',
}}
>
<span>Test</span>
</Box>
);
};
I initially started with just error.light and error.dark. When that didn't work, I expanded it all to palette.error.light, etc..., and then ultimately to theme.palette.error.light, etc....
It seems no matter what I try, it's not accessing those theme variables, and is instead just passing through the text.
So, back to the question: how am I supposed to access MUI 5 theme variables in nested functional components?

Replace
import { useTheme } from '#mui/styles';
with
import { useTheme } from '#mui/material/styles';
#mui/styles is used for legacy, you can add it using yarn add or npm install, but first give a shot to what I mentioned above.

Related

Mui theme not applying if within a wrapper component

With React (typescript) and MUI (5.4.2), I'm trying to put everything regarding styles within a single file, wrapping everything in my App.tsx.
Issue: The custom MUI theme does not apply to the rest of my app (fallback to default MUI theme)
The whole thing worked fine when the ThemeProvider component was placed directly within the App.tsx file, but broke as soon as I placed it elsewhere. I need to keep a separated component, for I'll add Elastic UI on top of MUI later on.
My App.tsx file:
function App() {
<UiProvider>
// ...whole app
</UiProvider>
}
The UiProvider component is a simple wrapper component as it follows:
import {ThemeProvider} from "#mui/styles";
import {CustomTheme} from "../../themes/CustomTheme";
import {createTheme, Theme} from "#mui/material/styles";
const UiProvider = (props: any) => {
return (
<ThemeProvider theme={CustomTheme}>
{props.children}
</ThemeProvider>
)
}
export default UiProvider
Because #mui/styles is the legacy styling solution for MUI, if this is for v5, perhaps the import for ThemeProvider should be:
import { ThemeProvider } from '#mui/material/styles';

can't change background color in theme Material-UI

I have been trying to define a custom theme using material UI and defining a default background colour for it. But the changes are not taking effect while other pallete options are working. Can anybody tell me what I'm doing wrong? As far as I can tell this is the way to change the colour.
Here's my code
theme.ts
import { createTheme } from '#mui/material';
import {red} from '#mui/material/colors';
const theme = createTheme({
palette: {
background: {
default: '#FFD600',
paper: '#FFD600',
},
},
});
export default theme;
my entry file
import '../styles/globals.css';
import {ThemeProvider} from "#mui/material/styles"
import theme from '../theme'
function MyApp({ Component, pageProps }) {
return (
<ThemeProvider theme={theme}>
<Component {...pageProps} />
</ThemeProvider>
);
}
export default MyApp;
EDIT: It seems to be changing the background for the components I have used with material-UI but not for other components I may define.
e.g. Here. I used the card component of MUI and the background was changed but I need it to change the background colour of the whole page
EDIT2: The component one worked because I defined paper colour, but I still cant get default to work
So the reason it's still not working is that we need to import CssBaseline for it to work. CssBaseline implements background.default color according to the doc.
Here's how it'll work
import '../styles/globals.css';
import {ThemeProvider} from "#mui/material/styles"
import theme from '../theme'
import { CssBaseline } from '#mui/material/';
function MyApp({ Component, pageProps }) {
return (
<ThemeProvider theme={theme}>
<CssBaseline/>
<Component {...pageProps} />
</ThemeProvider>
);
}
export default MyApp;
Still weird how the paper property works but for default you need to import additional dependencies

How to apply my custom themes with ConfigProvider in Ant Design?

We are developing a using interface with React and Ant Design. We would like to override the default theme and colors. As an example we defines an array called themes. There is 4 differents themes as object. I defined a button to change theme. Reading the official docs I have included the following code in App.js to override the default theme but no success.
ConfigProvider.config(
{
theme:theme
}
)
Can anyone help me to override the default theme without less or anything else but just with ConfigProvider?
import "./App.css";
import "antd/dist/antd.css";
import { ConfigProvider } from "antd";
import { ThemeProvider } from "styled-components";
import ChangeTheme from "./components/ChangeTheme";
import { useContext } from "react";
import { AppContext } from "./_context";
const App = () => {
const { theme } = useContext(AppContext);
ConfigProvider.config({
theme: theme,
});
return (
<ThemeProvider theme={theme}>
<ConfigProvider>
<div className="App">
<ChangeTheme />
</div>
</ConfigProvider>
</ThemeProvider>
);
};
export default App;
First Step
you need to import antd.variable.min.css in one of your root files, like src/index.js which is recommended in the document,
import 'antd/dist/antd.variable.min.css';
Second Step
you also need to import antd's ConfigProvider in src/index.js:
import { ConfigProvider } from 'antd';
Third Step
then you need to modify the theme variables in config method of ConfigProvider like below:
ConfigProvider.config({ theme: { primaryColor: "#f00" } });
ps the vriables that you can modify in theme are:
primaryColor
errorColor
infoColor
processingColor
successColor
warningColor
Fourth Step
finally you wrap ConfigProvider around the <App/> component in src/index.js
<ConfigProvider>
{/* some other codes you might have */}
<App />
</ConfigProvider>

Use style functions for material-ui components besides Box

I'm just starting with Material UI. Thanks for bearing with me.
I know you can use things like <Box mx={2}> out-of-the-box (ha). So if I wanted to put a margin around, say, a TextField, I could wrap it in a box.
Is there a simple way to set up my app's theme so that any component can use those style function props? (m, p, display, etc)
So that I could to <TextField mx={2}/> without having to wrap it in a Box.
The docs imply that you can do this:
(the example uses ThemeProvider from styled-components but I'm assuming that MUI's ThemeProvider works the same way???)
import React from 'react'
import { ThemeProvider } from 'styled-components'
const theme = {
spacing: 4,
palette: {
primary: '#007bff',
},
};
export default function App() {
return (
<ThemeProvider theme={theme}>
{/* children */}
</ThemeProvider>
)
}
I've tried this but it crashes from the TextField's my prop:
import { createMuiTheme, TextField, ThemeProvider } from '#material-ui/core';
// Greatly simplified version of my component
const App = () => <TextField my={2}/>
let theme = createMuiTheme({})
export default () =>
<ThemeProvider theme={ theme }>
<App/>
</ThemeProvider>;
I can do something like this and it works:
function App() {
const Input = styled(TextField)(compose(spacing))
return <Input my={3}/>
}
But then I'd have to compose my components every time I want to do use the style functions.
The docs are showing how the theme can parameterize the Box features (e.g. such that a spacing unit is 4px instead of 8px) -- the theme doesn't do anything to enable those features.
Material-UI is intending to support #material-ui/system features on core components in v5, but that is still months away.
Your main options are doing something like you showed in your example (though you should move const Input = styled(TextField)(compose(spacing)) to the top-level rather than doing this within render of App). You could put this in a separate file and import this component instead of TextField whenever you want to use those features. For instance:
MyTextField.js
import TextField from "#material-ui/core/TextField";
import { styled } from "#material-ui/core/styles";
import { compose, spacing } from "#material-ui/system";
export default styled(TextField)(compose(spacing));
App.js
import React from "react";
import TextField from "./MyTextField";
export default function App() {
return (
<div className="App">
<TextField variant="outlined" label="Material-UI system demo" />
</div>
);
}
Another option is to use Box with the clone prop and wrap the component you want to style. For instance:
import React from "react";
import TextField from "#material-ui/core/TextField";
import Box from "#material-ui/core/Box";
export default function App() {
return (
<div className="App">
<Box my={3} clone>
<TextField variant="outlined" label="Box demo" />
</Box>
</div>
);
}
You can also use the component prop of Box:
import React from "react";
import TextField from "#material-ui/core/TextField";
import Box from "#material-ui/core/Box";
export default function App() {
return (
<div className="App">
<Box my={3} component={TextField} variant="outlined" label="Box demo" />
</div>
);
}
Related answers:
Material-UI Grid does not hide whe use Display
Dynamic icon size in Material-UI
Box vs className vs style for vertical spacing in Material UI
Reusable component using theme-based Box features
Using Material-UI Box Component with the Drawer Compoment
How to use override Button using Box component in Material-UI?

Material UI nested theme providers breaks withStyles HOC

I have a React application created with Create React App and I use the #material-ui/core npm package for theming.
To customize components I use the withStyles higher-order component provided by MaterialUI.
According to documentation it supports nested ThemeProviders https://material-ui.com/customization/theming/#nesting-the-theme.
But inside the child ThemeProvider withStyles won't apply classes.
Here is a basic application demonstrating the issue -> https://codesandbox.io/s/vibrant-tree-eh83d
ExampleComponent.tsx
import React, { FunctionComponent } from "react";
import {
WithStyles,
withStyles,
createStyles,
StepButton,
Step,
Stepper,
Box
} from "#material-ui/core";
const styles = createStyles({
button: {
"& .MuiStepIcon-root.MuiStepIcon-active": {
fill: "red"
}
}
});
interface Props extends WithStyles<typeof styles> {
title: string;
}
const ExampleComponent: FunctionComponent<Props> = ({ title, classes }) => {
console.log(title, classes);
return (
<Box display="flex" alignItems="center">
<span>{title}</span>
<Stepper activeStep={0}>
<Step>
<StepButton className={classes.button}>Test</StepButton>;
</Step>
</Stepper>
</Box>
);
};
export default withStyles(styles)(ExampleComponent);
App.tsx
import * as React from "react";
import { ThemeProvider, createMuiTheme } from "#material-ui/core";
import ExampleComponent from "./ExampleComponent";
const theme = createMuiTheme();
function App() {
return (
<ThemeProvider theme={theme}>
<ExampleComponent title="Root" />
<ThemeProvider theme={theme}>
<ExampleComponent title="Nested" />
</ThemeProvider>
</ThemeProvider>
);
}
export default App;
Inside the ExampleComponent I console.log the generated classes object.
I want to use nested ThemeProviders and override classes inside components regardless of the ThemeProvider.
Am I missing something or is this not possible?
When you are using nested themes, you cannot reliably use Material-UI's global class names (e.g. .MuiStepIcon-root.MuiStepIcon-active). Within a nested theme, the "Mui..." class names have to be different to avoid conflicting with the CSS classes for the top-level theme since the nested theme will cause some of the CSS for the "Mui..." classes to be different.
You can use the following syntax in order to successfully match the suffixed versions of the Mui class names that occur within nested themes:
const styles = createStyles({
button: {
'& [class*="MuiStepIcon-root"][class*="MuiStepIcon-active"]': {
fill: "red"
}
}
});
Related answer:
How reliable are MUI Global Class names in JSS?

Resources