Dual Themes are very common nowadays in websites most commonly the light and dark versions and more and more apps and websites are including this feature. For React apps, the Material UI framework provides very useful functions using multiple themes, and switching between them is quite easy.
We’ll create a simple react app and switch between Dark and Light themes using a Switch component.
Creating React Application And Installing Module:
-
Step 1: Create a React application using the following command:
npx create-react-app gfg
-
Step 2: After creating your project folder i.e. gfg, move to it using the following command:
cd gfg
-
Step 3: After creating the ReactJS application, Install the material-ui modules using the following command:
npm install @material-ui/core
-
Step 4: Create the following two new files in the src folder:
- theme.js: In this file, we’ll define our theme objects.
- Component1.js: This is our example child component.
Project Structure: It will look like the following.
The useTheme Hook:
We can import multiple themes from our theme file and then switch themes in our parent component using a Switch component and its handler function. But a problem arises, what about its child components and further nested components?
In a single theme architecture, all components of an app can just import the theme object directly from the theme.js file and use it. Now, how would they know about the change in theme since there are dual themes? Would we have to pass down a prop notifying each component separately that ‘Hey, the active theme has changed’, That would be quite a cumbersome and inefficient way to do this.
Fortunately, Material-UI provides a hook useTheme through which we can access the parent theme variables inside our React Components. So we don’t need to import theme separately and can just use whatever theme object the parent component is using. Hence now even if we change themes it gets changed in the entire component tree.
Syntax:
import { useTheme } from '@material-ui/core/styles'; function Component1() { const theme = useTheme(); }
Creating the Dark theme object:
Even though Material UI comes with in-built light and dark modes we’ll define the dark theme object separately as moving ahead we can have the freedom to change every property distinctively in both themes, or you could even want a gray theme rather than a dark theme.
const darkTheme = responsiveFontSizes(createMuiTheme({ spacing: 4, }));
We’ll keep the fonts and typographies of the darkTheme same as the original one. We’ll change the background to black and the palette primary/secondary colors to white as shown below.
theme.js
import { createMuiTheme, responsiveFontSizes } from '@material-ui/core/styles' ; const lightTheme = responsiveFontSizes(createMuiTheme({ spacing: 4, typography: { fontFamily: [ 'Roboto' , 'Raleway' , 'Open Sans' , ].join( ',' ), h1: { fontSize: '5rem' , fontFamily: 'Raleway' , }, h3: { fontSize: '2.5rem' , fontFamily: 'Open Sans' , }, }, palette: { background: { default : '#009900' //green }, primary: { main: '#009900' , //green }, secondary:{ main: '#000000' , //black icons: '#009900' , //white }, text: { primary: '#000000' , //black secondary: '#FFFFFF' , //white }, }, })); const darkTheme = responsiveFontSizes(createMuiTheme({ spacing: 4, typography: { fontFamily: [ 'Roboto' , 'Raleway' , 'Open Sans' , ].join( ',' ), h1: { fontSize: '5rem' , fontFamily: 'Raleway' , }, h3: { fontSize: '2.5rem' , fontFamily: 'Open Sans' , }, }, palette: { background: { default : '#000000' //black }, primary: { main: '#FFFFFF' , //white }, secondary:{ main: '#FFFFFF' , //white icons: '#FFFFFF' , //white }, text: { primary: '#FFFFFF' , //white secondary: '#FFFFFF' , //white }, }, })); export default lightTheme ; export {darkTheme} ; |
Component1.js
import React, { Component } from 'react' ; import { makeStyles } from '@material-ui/core/styles' ; import { useTheme } from '@material-ui/core/styles' ; import Typography from '@material-ui/core/Typography' ; const useStyles = makeStyles((theme) => ({ root: { flexGrow: 1, }, })); export default function Component1() { const theme = useTheme(); const classes = useStyles(theme); return ( <div className={classes.root}> <Typography variant= "h3" align= "center" color= "textPrimary" paragraph> This is a Child Component text. </Typography> </div> ); } |
App.js
import React, { Component } from 'react' ; import './App.css' ; import CssBaseline from '@material-ui/core/CssBaseline' ; import { ThemeProvider } from '@material-ui/styles' ; import { createMuiTheme } from '@material-ui/core/styles' ; import Container from '@material-ui/core/Container' ; import Typography from '@material-ui/core/Typography' ; import Button from '@material-ui/core/Button' ; import { AppBar, Toolbar } from '@material-ui/core' ; import Switch from '@material-ui/core/Switch' ; import lightTheme, { darkTheme } from './theme' ; import Grid from '@material-ui/core/Grid' ; import Component1 from './Component1' ; function App() { // The 'checked' state is for the status of Switch component const [checked, setChecked] = React.useState( false ); // The 'newtheme' state tells if the new theme (i.e, dark theme) // is to be applied or not. const [newtheme, setTheme] = React.useState( false ); function changeTheme() { setTheme(!newtheme); setChecked(!checked); } // Conditional - if newtheme is set to true // then set appliedTheme to dark const appliedTheme = createMuiTheme(newtheme ? darkTheme : lightTheme); return ( <React.Fragment> <ThemeProvider theme={appliedTheme}> <CssBaseline /> <AppBar position= "static" color= "transparent" elevation={0}> <Toolbar> { /* Switch position whenever changed triggers the changeTheme() */ } <Switch checked={checked} onChange={() => { changeTheme() }} style={{ color: appliedTheme.palette.secondary.icons }} /> </Toolbar> </AppBar> <Container maxWidth= "sm" > <Typography component= "h1" variant= "h1" align= "center" color= "textPrimary" gutterBottom> Geeks for Geeks </Typography> <br /> <Component1></Component1> <br /> <Grid container direction= "row" justify= "center" spacing={4}> <Grid item> <Button variant= "contained" color= "secondary" > Button 1 </Button> </Grid> <Grid item> <Button variant= "outlined" color= "secondary" > Button 2 </Button> </Grid> </Grid> <br /> </Container> </ThemeProvider> </React.Fragment> ); } export default App; |
Step to Run Application: Run the application using the following command from the root directory of the project:
npm start
Output: Now open your browser and go to http://localhost:3000/, you will see the following output: