Hello Dev,
Now a days most of the websites have dark and light mode. In this post we will learn how to do that in your React application using the Context API.
Packages:
react-switch To implement the toggle switch (you can use button also)
react-icons To use sunny and moon icon (you can use any icon packages)
A quick demo
To access the repo here
Concept to understand here:
Context API - Provider and Consumer.
The Provider act as a Global state. So the Root component of the project should be wrapped by the Provider
Access the values stored in the provider using the Consumer at anywhere in your component
The provider always holds the value(state) and methods that modify the state
First create a Provider.js
Provider.js
import React, { useState, createContext } from "react";
export const ThemeContext = createContext();
const ThemeProvider = ({ children }) => {
const [mode, setTheme] = useState("light");
return (
<ThemeContext.Provider
value={{
mode,
setTheme: () => setTheme(mode === "dark" ? "light" : "dark")
}}
>
{children}
</ThemeContext.Provider>
);
};
export default ThemeProvider;
createContext()
creates a context object. Then defining a state
named mode
by default it as light value. And the setTheme
is an action that modifies the mode
value.
Then creating the Provider component by default it accepts value
as a prop. Notice that: The value
prop has mode
and setTheme
properties.
The provider component returns a children
. i.e where ever we wrap this provider that component will be considered as root
of this context provider.
From the above code we initialized ThemeContext
and Created our ThemeProvider
.
Create theme.js
theme.js
export const theme = {
light: {
color: "#03dac5",
backgroundColor: "#ffffff",
highlight: "#fdb813"
},
dark: {
color: "#bb86fc",
backgroundColor: "#121212",
highlight: "#ffffff"
}
};
Theme is just an object
containing values corresponding to the modes.
Wrap the ThemeProvider in the root component
index.js
import React from "react";
import ReactDOM from "react-dom";
import App from "./App";
import ThemeProvider from "./Provider";
const rootElement = document.getElementById("root");
ReactDOM.render(
<ThemeProvider>
<React.StrictMode>
<App />
</React.StrictMode>
</ThemeProvider>,
rootElement
);
Here, the root component is index.js
. Now the Provider is available globally, we can use the consumer in any where inside our components to access the value
from the provider.
Access the values from the provider
App.js
import React, { useContext } from "react";
import { theme } from "./theme";
import { ThemeContext } from "./Provider";
const getStyles = (mode) => ({
header: {
fontSize: 34,
fontWeight: "400"
},
app: {
height: "100%",
width: "100%",
padding: 16,
backgroundColor: theme[mode].backgroundColor
},
text: {
fontWeight: "200",
color: theme[mode].color
},
theme: {
color: theme[mode].highlight
}
});
export default function App() {
const { mode } = useContext(ThemeContext);
const styles = getStyles(mode);
return (
<div style={styles.app}>
<h1 style={(styles.header, styles.text)}>
Have a nice day... DEV!
</h1>
<h2 style={styles.text}>
Current theme is <span style={styles.theme}>{mode}</span> mode
</h2>
</div>
);
}
From the above code, we are trying to access the mode
value. So at first we need to say what context object we are trying to access(ThemeContext).
You notice that, mode
value is passed to styles
, here based on the mode
we are changing the text color and background color.
Time to create a toggle switch
ThemeSwitch.js
import React, { useContext } from "react";
import Switch from "react-switch";
import { IoMdSunny, IoMdMoon } from "react-icons/all";
import { ThemeContext } from "./Provider";
const getStyles = (mode) => ({
switch: {
display: "flex",
justifyContent: "center",
alignItems: "center",
height: "100%",
fontSize: 35,
paddingLeft: mode === "dark" ? 30 : 10
}
});
const ThemeSwitch = () => {
const { setTheme, mode } = useContext(ThemeContext);
const styles = getStyles(mode);
return (
<Switch
checked={mode === "light" ? true : false}
height={50}
width={120}
offColor="#1d1f2f"
onColor="#FDB813"
checkedIcon={
<IoMdSunny style={styles.switch} color="white" className="light" />
}
uncheckedIcon={
<IoMdMoon style={styles.switch} color="white" className="dark" />
}
onChange={setTheme}
/>
);
};
export default ThemeSwitch;
Here, we are handling the setTheme
method when the toggle switch is clicked. And based on the mode
we are updating the icons and colors.
Finally add the toggle switch inside the component
App.js
import React, { useContext } from "react";
import { theme } from "./theme";
import { ThemeContext } from "./Provider";
import ThemeSwitch from "./ThemeSwitch";
const getStyles = (mode) => ({
header: {
fontSize: 34,
fontWeight: "400"
},
app: {
height: "100%",
width: "100%",
padding: 16,
backgroundColor: theme[mode].backgroundColor
},
text: {
fontWeight: "200",
color: theme[mode].color
},
theme: {
color: theme[mode].highlight
}
});
export default function App() {
const { mode } = useContext(ThemeContext);
const styles = getStyles(mode);
return (
<div style={styles.app}>
<h1 style={(styles.header, styles.text)}>Have a nice day... DEV!</h1>
<h2 style={styles.text}>
Current theme is <span style={styles.theme}>{mode}</span> mode
</h2>
<ThemeSwitch />
</div>
);
}
Added <ThemeSwitch />
in App.js
. Now play with toggle switch to notice the changes.
That's all, you can add this ThemeSwitch
component any where inside your project to change the theme.
🎉 Tada... We are done...
Thanks 😃 for the read...
Drop a ♥️ if this content is helpful...
Suggestions and doubts are always welcome in the comment section...
Top comments (2)
Just add localStorage usage to it and you have a theme setting that persists across page refresh 😁.
That is great idea... thanks. Will update the post soon