I am new to react and building a React app using Material-UI (MUI v5) and have implemented routing using React Router. I want to include a theme switcher(which is present in SignUp and SignIn MUI Templates) that allows toggling between Light, Dark, and System modes globally . However, the theme switcher doesn’t seem to work as expected, and the theme isn’t applied consistently across all pages when i am trying to do so.
index.js
import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
import App from './App';
import Home from './Home';
import SignIn from './sign-in/SignIn';
import SignUp from './sign-up/SignUp';
import reportWebVitals from './reportWebVitals';
import {BrowserRouter, Route, Routes} from 'react-router-dom';
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<React.StrictMode>
<BrowserRouter>
<App />
<Routes>
<Route path = "/" element={<Home/>}/>
<Route path = "sign-up" element={<SignUp/>}/>
<Route path = "sign-in" element={<SignIn/>}/>
</Routes>
</BrowserRouter>
</React.StrictMode>
);
reportWebVitals();
SignUp.js
import * as React from 'react';
import Box from '@mui/material/Box';
import Button from '@mui/material/Button';
import Checkbox from '@mui/material/Checkbox';
import CssBaseline from '@mui/material/CssBaseline';
import Divider from '@mui/material/Divider';
import FormControlLabel from '@mui/material/FormControlLabel';
import FormLabel from '@mui/material/FormLabel';
import FormControl from '@mui/material/FormControl';
import Link from '@mui/material/Link';
import TextField from '@mui/material/TextField';
import Typography from '@mui/material/Typography';
import Stack from '@mui/material/Stack';
import MuiCard from '@mui/material/Card';
import { styled } from '@mui/material/styles';
import AppTheme from '../shared-theme/AppTheme';
import ColorModeSelect from '../shared-theme/ColorModeSelect';
import { GoogleIcon, FacebookIcon } from './CustomIcons';
const Card = styled(MuiCard)(({ theme }) => ({
display: 'flex',
flexDirection: 'column',
alignSelf: 'center',
width: '100%',
padding: theme.spacing(4),
gap: theme.spacing(2),
margin: 'auto',
boxShadow:
'hsla(220, 30%, 5%, 0.05) 0px 5px 15px 0px, hsla(220, 25%, 10%, 0.05) 0px 15px 35px -5px',
[theme.breakpoints.up('sm')]: {
width: '450px',
},
...theme.applyStyles('dark', {
boxShadow:
'hsla(220, 30%, 5%, 0.5) 0px 5px 15px 0px, hsla(220, 25%, 10%, 0.08) 0px 15px 35px -5px',
}),
}));
const SignUpContainer = styled(Stack)(({ theme }) => ({
height: 'calc((1 - var(--template-frame-height, 0)) * 100dvh)',
minHeight: '100%',
padding: theme.spacing(2),
[theme.breakpoints.up('sm')]: {
padding: theme.spacing(4),
},
'&::before': {
content: '""',
display: 'block',
position: 'fixed',
zIndex: -1,
inset: 0,
backgroundImage:
'radial-gradient(ellipse at 50% 50%, hsl(210, 100%, 97%), hsl(0, 0%, 100%))',
backgroundRepeat: 'no-repeat',
...theme.applyStyles('dark', {
backgroundImage:
'radial-gradient(at 50% 50%, hsla(210, 100%, 16%, 0.5), hsl(220, 30%, 5%))',
}),
},
}));
export default function SignUp(props) {
const [emailError, setEmailError] = React.useState(false);
const [emailErrorMessage, setEmailErrorMessage] = React.useState('');
const [passwordError, setPasswordError] = React.useState(false);
const [passwordErrorMessage, setPasswordErrorMessage] = React.useState('');
const [nameError, setNameError] = React.useState(false);
const [nameErrorMessage, setNameErrorMessage] = React.useState('');
const validateInputs = () => {
const email = document.getElementById('email');
const password = document.getElementById('password');
const name = document.getElementById('name');
let isValid = true;
if (!email.value || !/S+@S+.S+/.test(email.value)) {
setEmailError(true);
setEmailErrorMessage('Please enter a valid email address.');
isValid = false;
} else {
setEmailError(false);
setEmailErrorMessage('');
}
if (!password.value || password.value.length < 6) {
setPasswordError(true);
setPasswordErrorMessage('Password must be at least 6 characters long.');
isValid = false;
} else {
setPasswordError(false);
setPasswordErrorMessage('');
}
if (!name.value || name.value.length < 1) {
setNameError(true);
setNameErrorMessage('Name is required.');
isValid = false;
} else {
setNameError(false);
setNameErrorMessage('');
}
return isValid;
};
const handleSubmit = (event) => {
if (nameError || emailError || passwordError) {
event.preventDefault();
return;
}
const data = new FormData(event.currentTarget);
console.log({
name: data.get('name'),
lastName: data.get('lastName'),
email: data.get('email'),
password: data.get('password'),
});
};
return (
<AppTheme {...props}>
<CssBaseline enableColorScheme />
<ColorModeSelect sx={{ position: 'fixed', top: '1rem', right: '1rem' }} />
<SignUpContainer direction="column" justifyContent="space-between">
<Card variant="outlined">
<div class="logo"></div>
<Typography
component="h1"
variant="h4"
sx={{ width: '100%', fontSize: 'clamp(2rem, 10vw, 2.15rem)' }}
>
Sign up
</Typography>
<Box
component="form"
onSubmit={handleSubmit}
sx={{ display: 'flex', flexDirection: 'column', gap: 2 }}
>
<FormControl>
<FormLabel htmlFor="name">Full name</FormLabel>
<TextField
autoComplete="name"
name="name"
required
fullWidth
id="name"
placeholder="Jon Snow"
error={nameError}
helperText={nameErrorMessage}
color={nameError ? 'error' : 'primary'}
/>
</FormControl>
<FormControl>
<FormLabel htmlFor="email">Email</FormLabel>
<TextField
required
fullWidth
id="email"
placeholder="[email protected]"
name="email"
autoComplete="email"
variant="outlined"
error={emailError}
helperText={emailErrorMessage}
color={passwordError ? 'error' : 'primary'}
/>
</FormControl>
<FormControl>
<FormLabel htmlFor="password">Password</FormLabel>
<TextField
required
fullWidth
name="password"
placeholder="••••••"
type="password"
id="password"
autoComplete="new-password"
variant="outlined"
error={passwordError}
helperText={passwordErrorMessage}
color={passwordError ? 'error' : 'primary'}
/>
</FormControl>
<FormControlLabel
control={<Checkbox value="allowExtraEmails" color="primary" />}
label="I want to receive updates via email."
/>
<Button
type="submit"
fullWidth
variant="contained"
onClick={validateInputs}
>
Sign up
</Button>
</Box>
<Divider>
<Typography sx={{ color: 'text.secondary' }}>or</Typography>
</Divider>
<Box sx={{ display: 'flex', flexDirection: 'column', gap: 2 }}>
<Button
fullWidth
variant="outlined"
onClick={() => alert('Sign up with Google')}
startIcon={<GoogleIcon />}
>
Sign up with Google
</Button>
<Button
fullWidth
variant="outlined"
onClick={() => alert('Sign up with Facebook')}
startIcon={<FacebookIcon />}
>
Sign up with Facebook
</Button>
<Typography sx={{ textAlign: 'center' }}>
Already have an account?{' '}
<Link
href="/sign-in/"
variant="body2"
sx={{ alignSelf: 'center' }}
>
Sign in
</Link>
</Typography>
</Box>
</Card>
</SignUpContainer>
</AppTheme>
);
}
I think the theme switcher is from colormodeselect component which I am attaching here,
ColorModeSelect.js
import * as React from 'react';
import { useColorScheme } from '@mui/material/styles';
import MenuItem from '@mui/material/MenuItem';
import Select from '@mui/material/Select';
export default function ColorModeSelect(props) {
const { mode, setMode } = useColorScheme();
if (!mode) {
return null;
}
return (
<Select
value={mode}
onChange={(event) => setMode(event.target.value)}
SelectDisplayProps={{
'data-screenshot': 'toggle-mode',
}}
{...props}
>
<MenuItem value="system">System</MenuItem>
<MenuItem value="light">Light</MenuItem>
<MenuItem value="dark">Dark</MenuItem>
</Select>
);
}
Please guide me with the changes I have to do
I had tried
Wrapping the entire app with ThemeProvider.
Adding the ColorModeSelect component to the App and Home components.
Leo is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.
1