I just can't figure it out. I want to change background with Switch, but it works only the first time, doesn't work on subsequent clicks.
Here is code sandbox:
https://codesandbox.io/s/strange-shaw-2tfk5
Could someone enlighten me what is going on? I'm using Material UI with React
minimal reproducible example:
this is my App component
import React from "react";
import { useState } from "react";
import { ThemeProvider, createTheme } from "#material-ui/core/styles";
import { Container, Switch, CssBaseline } from "#material-ui/core";
const darkTheme = createTheme({
palette: {
type: "dark",
background: {
default: "hsl(230, 17%, 14%)"
}
}
});
const lightTheme = createTheme({
palette: {
type: "light",
background: {
default: "hsl(0, 0%, 100%)"
}
}
});
const App = () => {
const [mode, setMode] = useState("light");
const selectedTheme = mode === "dark" ? darkTheme : lightTheme;
return (
<ThemeProvider theme={selectedTheme}>
<CssBaseline />
<Container maxWidth="lg">
<h1>Hello</h1>
<Switch onChange={() => setMode(mode === "light" ? "dark" : "light")} />
</Container>
</ThemeProvider>
);
};
export default App;
According to the Material UI documentation, to switch between light and dark, they suggest useMemo to create a new theme on demand:
import { useState, useMemo } from "react";
function App() {
const [mode, setMode] = useState("light");
const theme = useMemo(
() =>
createTheme({
palette: {
type: mode,
background: {
dark: "hsl(230, 17%, 14%)",
light: "hsl(0, 0%, 100%)"
}
}
}),
[mode]
);
return (
<ThemeProvider theme={theme}>
<CssBaseline />
<Container maxWidth="lg">
<h1>Hello</h1>
<Switch onChange={() => setMode(mode === "light" ? "dark" : "light")} />
</Container>
</ThemeProvider>
);
}
This works even when using StrictMode.
The reason it doesn't work with StrictMode when defined outside of the component is addressed in a Github issue, which states that this behaviour should be fixed in v5 (still in beta).
not sure why, but removing StrictMode in index.js fixed the problem
You can either put your variables for themes inside your App component:
https://codesandbox.io/s/tender-lederberg-8v7tx?file=/src/App.js
or dynamically change theme properties:
https://codesandbox.io/s/magical-ives-587fm?file=/src/App.js
I'm using Material-Ui in my React Project !
i followed the steps in the documentation to allow RTL in my project and all work fine !
except the TextField Component
LTR DIRECTION :
RTL DIRECTION
Like you see ! the problem is with the label still in left ( the input text work fine )
App.js file
import React, {useState} from 'react';
//i18n
import {withTranslation} from "react-i18next";
import './i18n';
//jss
import { create } from 'jss';
import rtl from 'jss-rtl';
import { StylesProvider, jssPreset } from '#material-ui/core/styles';
// Configure JSS
const jss = create({ plugins: [...jssPreset().plugins, rtl()] });
function App(props) {
// initialize Language
const { i18n } = props;
const [ prefLang, setPrefLang] = useState(i18n.language);
let theme =createMuiTheme({
palette : {
primary : {
main : '#ed5ac0',
},
},
typography : {
fontFamily : "lalezar, cursive",
h3 : {
fontSize : 1.4,
},
h4 : {
fontSize : 1.5
},
fontAwseomeSize : {
xs : "14px",
sm : "14px",
md : "16px"
},
responsiveFont : {
xs : "20px",
sm : "12.5px",
md : "14px"
},
highLight : {
md : "25px"
},
subHighLight : {
md : "18px"
}
},
}
);
return (
<BrowserRouter>
<LangContext.Provider
value ={{
prefLang,
setPrefLang
}}
>
<CssBaseline/>
<ThemeProvider theme={theme}>
<StylesProvider jss={jss}>
<Grid dir={(prefLang === "ar") ? "rtl" : "ltr"}>
{/*<AppHeader/>*/}
<ContentRouter/>
</Grid>
</StylesProvider>
</ThemeProvider>
</LangContext.Provider>
</BrowserRouter>
);
}
export default withTranslation()(App);
My Form Component
const LoginForm = () => {
return (
<>
<Backdrop style={{ zIndex : 999 , color : theme.palette.primary.main}} open={backdrop} >
<CircularProgress color="inherit" />
</Backdrop>
<form onSubmit={formik.handleSubmit} style={{width: "100%", marginTop: "20px"}}>
{ userNotFound ? <Alert style={{marginBottom : "20px"}} variant="outlined" severity="error">
This is an error alert — check it out!
</Alert> : null}
<TextField
id="identifier"
name="identifier"
onChange={formik.handleChange}
value={formik.values.identifier}
label={t('formIdentifier')}
fullWidth
/>
{formik.touched.identifier && formik.errors.identifier ?
(
<Alert style={{ marginTop :"10px"}} variant="outlined" severity="error">{formik.errors.identifier}</Alert>
) : null}
<TextField
style={{marginTop: "50px"}}
id="password"
name="password"
type="password"
onChange={formik.handleChange}
value={formik.values.password}
label={t('formPassword')}
fullWidth
/>
{formik.touched.password && formik.errors.password ?
(
<Alert style={{ marginTop :"10px"}} variant="outlined" severity="error">{formik.errors.password}</Alert>
) : null}
<Button type="submit" color="primary">{t('login')}</Button>
</form>
</>
);
};
My Theme.js File
import createMuiTheme from "#material-ui/core/styles/createMuiTheme";
let theme =createMuiTheme({
direction : 'rtl',
palette : {
primary : {
main : '#ed5ac0',
},
},
typography : {
fontFamily : "Merienda One, sans-serif",
h3 : {
fontSize : 1.4,
},
h4 : {
fontSize : 1.5
},
fontAwseomeSize : {
xs : "14px",
sm : "14px",
md : "16px"
},
responsiveFont : {
xs : "20px",
sm : "12.5px",
md : "14px"
},
highLight : {
md : "40px"
}
},
}
);
export default theme;
Any suggestion to make the label RTL ?
The documentation contains four steps for rtl support:
Set the dir attribute on the body element.
In my examples below, this is handled by the following:
React.useLayoutEffect(() => {
document.body.setAttribute("dir", isRtl ? "rtl" : "ltr");
}, [isRtl]);
Set the direction in the theme.
In my examples below, I am toggling between two themes:
const ltrTheme = createTheme({ direction: "ltr" });
const rtlTheme = createTheme({ direction: "rtl" });
...
<ThemeProvider theme={isRtl ? rtlTheme : ltrTheme}>
...
</ThemeProvider>
Install the rtl plugin.
For v4 (using JSS), this means installing jss-rtl.
For v5 (using Emotion), this means installing stylis-plugin-rtl.
Load the rtl plugin.
Below is a v4 example showing how to load the rtl plugin for JSS (v5 example further down).
For performance reasons it is important to avoid re-rendering StylesProvider, so this should not be in a component with state that can change and therefore trigger a re-render. In my own app, I have the StylesProvider element in my index.js file as the first element inside the call to react-dom render.
import rtl from "jss-rtl";
import {
StylesProvider,
jssPreset
} from "#material-ui/core/styles";
// Configure JSS
const jss = create({ plugins: [...jssPreset().plugins, rtl()] });
export default function App() {
return (
<StylesProvider jss={jss}>
<AppContent />
</StylesProvider>
);
}
The example below includes a TextField and you can see that the label's position toggles correctly.
import React from "react";
import { create } from "jss";
import rtl from "jss-rtl";
import {
StylesProvider,
jssPreset,
ThemeProvider,
createTheme
} from "#material-ui/core/styles";
import CssBaseline from "#material-ui/core/CssBaseline";
import TextField from "#material-ui/core/TextField";
import Button from "#material-ui/core/Button";
import Box from "#material-ui/core/Box";
// Configure JSS
const jss = create({ plugins: [...jssPreset().plugins, rtl()] });
const ltrTheme = createTheme({ direction: "ltr" });
const rtlTheme = createTheme({ direction: "rtl" });
function AppContent() {
const [isRtl, setIsRtl] = React.useState(false);
const [value, setValue] = React.useState("initial value");
React.useLayoutEffect(() => {
document.body.setAttribute("dir", isRtl ? "rtl" : "ltr");
}, [isRtl]);
return (
<ThemeProvider theme={isRtl ? rtlTheme : ltrTheme}>
<CssBaseline />
<Box m={2}>
<TextField
variant="outlined"
value={value}
onChange={(event) => setValue(event.target.value)}
label={isRtl ? "بريد الكتروني او هاتف" : "Email or Phone"}
/>
<br />
<br />
Current Direction: {isRtl ? "rtl" : "ltr"}
<br />
<Button onClick={() => setIsRtl(!isRtl)}>Toggle direction</Button>
</Box>
</ThemeProvider>
);
}
export default function App() {
return (
<StylesProvider jss={jss}>
<AppContent />
</StylesProvider>
);
}
Below is an equivalent example for v5 using Emotion.
import React from "react";
import rtlPlugin from "stylis-plugin-rtl";
import { CacheProvider } from "#emotion/react";
import createCache from "#emotion/cache";
import { ThemeProvider, createTheme } from "#mui/material/styles";
import CssBaseline from "#mui/material/CssBaseline";
import TextField from "#mui/material/TextField";
import Button from "#mui/material/Button";
import Box from "#mui/material/Box";
import { prefixer } from "stylis";
const cacheLtr = createCache({
key: "muiltr"
});
const cacheRtl = createCache({
key: "muirtl",
// prefixer is the only stylis plugin by default, so when
// overriding the plugins you need to include it explicitly
// if you want to retain the auto-prefixing behavior.
stylisPlugins: [prefixer, rtlPlugin]
});
const ltrTheme = createTheme({ direction: "ltr" });
const rtlTheme = createTheme({ direction: "rtl" });
export default function App() {
const [isRtl, setIsRtl] = React.useState(false);
const [value, setValue] = React.useState("initial value");
React.useLayoutEffect(() => {
document.body.setAttribute("dir", isRtl ? "rtl" : "ltr");
}, [isRtl]);
return (
<CacheProvider value={isRtl ? cacheRtl : cacheLtr}>
<ThemeProvider theme={isRtl ? rtlTheme : ltrTheme}>
<CssBaseline />
<Box m={2}>
<TextField
value={value}
onChange={(event) => setValue(event.target.value)}
label={isRtl ? "بريد الكتروني او هاتف" : "Email or Phone"}
/>
<br />
<br />
Current Direction: {isRtl ? "rtl" : "ltr"}
<br />
<Button onClick={() => setIsRtl(!isRtl)}>Toggle direction</Button>
</Box>
</ThemeProvider>
</CacheProvider>
);
}
In addition, I have a later answer that discusses flipping icons: material-ui icons won't flip when I change to RTL.
Another solution I've found to set this for specific component is to add it via native JS after component is rendered.
First I created a ref to the input element:
const inputRef = createRef()
<TextField inputRef={inputRef} />
Then added a useEffect hook to perform once on each render:
useEffect(() => {
if(inputRef)
inputRef.current.dir = 'auto'
}, [])
Not the most beautiful code, but it sure works 😉
RTL for specific components, and not the whole app:
MUI rtl documentation and the accepted answer pertain to the entire application.
If you want just a few components here and there, all you need to do is create a simple wrapper component, and children will have rtl. No need for a theme provider.
Just be sure to wrap the children with a <div dir="rtl">.
Wrapper Component:
import React from 'react';
import rtlPlugin from "stylis-plugin-rtl";
import { CacheProvider } from "#emotion/react";
import createCache from "#emotion/cache";
import { prefixer } from 'stylis'
const cacheRtl = createCache({
key: "muirtl",
stylisPlugins: [prefixer, rtlPlugin]
});
const RtlProvider = ({ children }) => {
return (
<CacheProvider value={cacheRtl}>
<div dir="rtl">
{children}
</div>
</CacheProvider>
);
};
export default RtlProvider;
Simple Implementation
return (
<RtlProvider>
<TextField label="my label"/>
</RtlProvider>
)
Making CacheProvider tag as a youngest parent for your content, such like below:
<ThemeProvider>
<ScrollToTop />
<BaseOptionChartStyle />
<CacheProvider value={cacheRtl}>
<Router />
</CacheProvider>
</ThemeProvider>
*note:
(Router) includes the various textFields
apart from theme and jss-rtl and stuff that Ryan Cogswell said, there's one tiny problem that i found no where after days of searching (just 3-4 days actualy :)).
and that is the import statement.
make sure youre import statement is like the following:
import TextField from "#material-ui/core/TextField"; //true
if youre import statement is like following, none of those theming and jss stuff works:
import TextField from "#mui/material/TextField"; //false
I am new to React and MUI and maybe I am just missing something.
I am trying to make a button with color='warning' that is defined in my palette like this (the theme works and I can use primary and secondary colors):
const theme = createMuiTheme({
palette: {
primary: {
main: '#70B657'
},
secondary: {
light: '#2face3',
main: '#4199D8',
contrastText: '#ffcc00'
},
warning: {
main: '#BC211D'
}
}
});
I noticed in the documentation that the <Button> color prop only takes default|inherit|primary|secondary so it is not possible to use it like that.
So what is the CORRECT or best practice to use warning colored button in Material-UI? I think this is a basic thing and should be pretty easy to achieve..??
Preferably a solution that does not involve making several different Themes and importing them when needed.
Thanks!
Usage:
const useStyles = makeStyles(theme => ({
root: {
color: theme.palette.warning.main
}
}));
Full code:
import React from "react";
import "./styles.css";
import { Button } from "#material-ui/core";
import { createMuiTheme, ThemeProvider } from "#material-ui/core/styles";
import { makeStyles } from "#material-ui/core/styles";
const useStyles = makeStyles(theme => ({
root: {
color: theme.palette.warning.main
}
}));
function YourComponent() {
const classes = useStyles();
return (
<div className="App">
<Button variant="contained" classes={{ root: classes.root }}>
Secondary
</Button>
</div>
);
}
const theme = createMuiTheme({
palette: {
warning: { main: "#FFFFFF" }
}
});
export default function App() {
return (
<ThemeProvider theme={theme}>
<YourComponent />
</ThemeProvider>
);
}
Update
Pass props to makeStyles
import React from "react";
import "./styles.css";
import { makeStyles } from "#material-ui/core/styles";
const useStyles = props =>
makeStyles(theme => ({
root: {
color: props.value === "111" ? "red" : "blue"
}
}));
const Comp = props => {
const classes = useStyles(props)();
return <input defaultValue={props.value} className={classes.root} />;
};
export default function App() {
return (
<div className="App">
<div>
<Comp value={"111"} />
</div>
<div>
<Comp value={"222"} />
</div>
</div>
);
}
yeah I don't understand why the first example would work and the second dont.
app component
const theme = createMuiTheme({
palette: {
primary: {
main: '#bed000',
},
secondary: {
main: '#110b36',
},
error: {
main: '#B33A3A',
},
},
})
<MuiThemeProvider theme={theme}>
<Route exact path="/" component={LoginView} />
</MuiThemeProvider>
<LoginView>
<TextField
autoFocus
label="Contraseña"
name="Password"
type="Password"
value={values.Password}
onChange={handleChange}
onBlur={handleBlur}
fullWidth
color={touched.Password && errors.Password ? "primary" : "secondary"}
/>
<TextField
autoFocus
label="Contraseña"
name="Password"
type="Password"
value={values.Password}
onChange={handleChange}
onBlur={handleBlur}
fullWidth
color={touched.Password && errors.Password ? "error" : "secondary"}
/>
</LoginView>
I'm new to React and Material UI, i'm trying to add some custom styles to the MUI buttons by using the createMuiTheme.
I've followed the docs and pretty much copied the example but it's having no effect and no errors are being thrown in the console.
I've been banging my head against this for a while now and I can't see what the problem is, what am I missing?
import React from 'react';
import { createMuiTheme } from '#material-ui/core/styles';
import { ThemeProvider } from '#material-ui/styles';
import Button from '#material-ui/core/Button';
const mytheme = createMuiTheme({
palette: {
primary: { main: '#1565C0'},
secondary: { main: '#11cb5f' },
},
});
export const PrimaryButton = (props) => {
return (
<ThemeProvider theme={mytheme}>
<a href={props.buttonLink}>
<Button
style={{ ...props.styles}}
onClick={props.handleClick}
variant='contained' color='primary' size='large'
>
{props.buttonText}
</Button>
</a>
</ThemeProvider>
);
};
export const SecondaryButton = (props) => {
return (
<ThemeProvider theme={mytheme}>
<Button
style={{...props.styles }}
value={props.value || ''}
onClick={props.handleClick}
variant='outlined' color='secondary' size='large'
>
{props.buttonText}
</Button>
</ThemeProvider>
)
}
It is working. Try to change this:
variant='outlined'
by
variant='contained'
I am trying to use a navbar from the Material UI collection however the component was written as a function and was using hooks. I'm trying to convert the component to a HOC (class) and I am having issues with accessing the theme in the component. Theme in the component in undefined
const styles = theme => ({
root: {
display: "flex"
},
});
<IconButton onClick={this.handleDrawerClose}>
{theme.direction === "ltr" ? (
<ChevronLeftIcon />
) : (
<ChevronRightIcon />
)}
</IconButton>
Try this:
import React from 'react';
import PropTypes from 'prop-types';
import { withStyles } from '#material-ui/core';
import Paper from './Paper';
const styles = () => ({
root: {
display: 'flex'
}
});
const Bubble = props => {
const { classes } = props;
return (
<IconButton className={classes.root} onClick={this.handleDrawerClose}></IconButton>
);
};
export default withStyles(styles)(Bubble);