Temitope Ayodele
Posted on January 26, 2021
Originally written on my blog.
Many people prefer to read on a dark screen, while others prefer the light mode. Giving users the ability to switch between these two modes is a great user experience feature. It is pretty easy to implement this in your code, and in this article, I will work you through how to do this in React using styled-components
. I used this method to implement this on my portfolio website
To begin install styled-components
npm install styled-components
You can check out documentation of styled-components
We will now create some components
1. Theme Component
This component will contain your preferred colors for dark mode and light mode.
// theme.js
export const lightTheme = {
body: "#fffffe",
header: "#094067",
text: "#5f6c7b",
button: "#3da9fc"
};
export const darkTheme = {
body: "#094067",
header: "#fffffe",
text: "#d8eefe",
button: "#3da9fc"
};
2. Wrapping the app with ThemeProvider
To make this theme available to all pages, the component(in our case, App.js) is wrapped in the ThemeProvider
. This way, all styled-components within the ThemeProvider has access to the provided theme, no matter how deep.
import { ThemeProvider } from "styled-components";
import { lightTheme, darkTheme } from "./theme";
///STYLED-COMPONENTS
import { H1, Layout, P } from "./styles";
export default function App() {
return (
<ThemeProvider theme={lightTheme}>
<Layout>
<H1>My Awesome App</H1>
<P>
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Fusce vel....
</P>
<Button>Toggle mode</Button>
</Layout>
</ThemeProvider>
);
}
In the above snippet, we wrapped the component with the ThemeProvider and passed the lightTheme to the theme prop.
3. Create Global Stylesheet
With styled components, you can specify global styles that spans across your application. You do this by importing createGlobalStyle
from styled-components. Now that our app is wrapped withing the ThemeProvider
, every component within it now has access to the theme.
// GlobalStyles.js
import { createGlobalStyle} from "styled-components"
export const GlobalStyles = createGlobalStyle`
body {
background: ${({ theme }) => theme.body};
color: ${({ theme }) => theme.text};
font-family: Roboto, sans-serif;
transition: all 0.4s linear;
}
`
4. Accessing the theme prop for styled-components
We can also go ahead to defined colors on our styled-components using the theme props
// styles.js
import styled from "styled-components";
export const Layout = styled.div`
width: 100%;
height: 100vh;
text-align: center;
padding: 2%;
box-sizing: border-box;
`;
export const H1 = styled.h1`
font-size: 2rem;
color: ${(props) => props.theme.header};
`;
export const P = styled.p`
font-size: 1.2rem;
color: ${(props) => props.theme.text};
`;
export const Button = styled.button`
border: none;
padding: 0.7rem 1rem;
background: ${(props) => props.theme.button};
border-radius: 5px;
font-weight: 700;
font-size: 1rem;
color: ${(props) => props.theme.body};
`;
TOGGLE BETWEEN LIGHT MODE AND DARKMODE
To toggle between the two modes, we can use a custom hook, called the useDarkMode.
// useDarkMode.js
import { useEffect, useState } from "react";
export const useDarkMode = () => {
const [theme, setTheme] = useState("light");
const [componentMounted, setComponentMounted] = useState(false);
const setMode = (mode) => {
window.localStorage.setItem("theme", mode);
setTheme(mode);
};
const toggleTheme = () => {
if (theme === "light") {
setMode("dark");
} else {
setMode("light");
}
};
useEffect(() => {
const localTheme = window.localStorage.getItem("theme");
if (localTheme) {
setTheme(localTheme);
} else {
setMode("light");
}
setComponentMounted(true);
}, []);
return [theme, toggleTheme, componentMounted];
};
-
setMode
saves the user's preferred theme in localStorage. This ensures that when the user selects a theme, the preferred choice persists even after the user leaves the app. -
toggleTheme
function toggles between light theme and dark theme -
useEffect
lifecycle hook checks on component mounting if there is a previuosly stored theme in the localStorage, if yes, the theme is set to that value. If there isn't the theme is set to light (or dark if you please)
Next we import this custom hook to the App.js
import React, { useEffect } from "react";
import { Button, H1, Layout, P } from "./styles";
import { ThemeProvider } from "styled-components";
import { lightTheme, darkTheme } from "./theme";
import { GlobalStyles } from "./GlobalStyles";
import { useDarkMode } from "./useDarkMode";
export default function App() {
//New
const [theme, toggleTheme, componentMounted] = useDarkMode();
useEffect(() => {
if (!componentMounted) {
return <div />;
}
// eslint-disable-next-line
}, []);
//..New
return (
<ThemeProvider theme={theme === 'light' ? lightTheme : darkTheme}>
<GlobalStyles />
<Layout>
<H1>My Awesome App</H1>
<P>
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Fusce vel...
</P>
<Button onClick={() => toggleTheme()}>Toggle mode</Button>
</Layout>
</ThemeProvider>
);
}
In the above snippet, we imported the useDarkMode custom hook which returns the theme, the toggle functionallity and the componentMounted.
- First, we confirm that the component has mounted using the useEffect lifecycle hook. If it hasn't, we render an empty div.
- We add the toggleTheme functionality to the button, which toggles the theme onClick(light and dark mode),
- then in the ThemeProvider, we dynamically render lightTheme or darkTheme based on the theme returned by the useDarkMode hook.
That is all! We can now easily toggle between the light mode and the darkmode. Below is the codesandbox for the full code.
Posted on January 26, 2021
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.