It's becoming harder to see a webapp, website or native app without light and dark modes.
Theming is now a must have for projects, clients are asking for it, UI designers are making designs for dark and light modes for every page/state in the project.
- Is applying themes in a project a big deal?
- Is it hard?
- I just started writing react, and I don't even know how to use themes in vanilla js and you are saying i should use it for this project.
- How do i do this?
All these and more are the questions that plague the developer, I should know, because I asked same and more.
wink.
We will discuss an easy and straightforward way of implementing themes in react apps using the
context api
.
First Initialize the project
npx create-react-app theme-app
Then, install the dependencies;
- react-router-dom,
- any other you want...
Create a themeContext.js
file
This is where we write our theme context for both light and dark modes or the plethora of modes we want. Also, our colours go here.
In this file, because we are using functional and not class components, we import react's useState
hook to handle theme mode
.
import React, { useState } from 'react'
Set color for theme states
Then, write a themes object that contains all the themes you want to use.
Since we are only using light
and dark
modes, we will create our themes object as:
const themes = {
light: {
background: '#ffffff',
lightFont: '#716E8B',
primaryFont: '#22202D',
secondaryFont: '#393649',
buttonFont: '#442ECF',
},
dark: {
background: '#222222',
lightFont: '#B4B0D1',
primaryFont: '#ffffff',
buttonFont: '#ffffff',
secondaryFont: '#F5F4FA',
},
};
Get user's preferred theme
Next, we declare our defaultTheme
. This will be used to set the theme to either light
or dark
based on the user's presets.
let defaultTheme;
Then check what theme mode user prefers using
if (window.matchMedia('(prefers-color-scheme: dark)').matches) {
defaultTheme = true;
} else {
defaultTheme = false;
}
This checks, if the user prefers the dark mode.
window.matchMedia('(prefers-color-scheme: dark)').matches
returns either true
if the user has set his device to dark mode or false
if the user has set his device to light mode instead.
If the mode is dark, we set defaultTheme
to true. we'll understand why soon.
Create themeContext
export const themeContext = React.createContext();
This creates our context and exports it so we can use it in other files to access our theme.
If you don't understand how contexts work in react, you can read the official react doc while i work on an article on the subject.
Write Theme.Provider
This is used to provide our theme context to descendant components.
export const ThemeProvider = props => {
const [isDark, setIsDark] = useState(defaultTheme);
let theme;
if (isDark) {
theme = themes.dark;
} else {
theme = themes.light;
}
const toggleTheme = () => setIsDark(!isDark);
return (
<themeContext.Provider value={{
theme,
toggleTheme,
isDark
}}>
{props.children}
</themeContext.Provider>
)
}
The first thing we are doing is creating a state variable isDark
and setting it to defaultTheme
which is true
if the user set his device to dark mode and false
if the device is in light mode.
Hence, isDark
is true
if the device is set to dark mode and vice versa.
Then, we declare theme
and checking if isDark
is true
, set theme to the dark object in the themes object or do the else, if isDark
is false
.
Next, we write a toggleTheme
function to change isDark
when called.
In, the return statement, we are using themeContext.Provider
to pass theme, toggleTheme and isDark
to descendants.
This is done by passing them as an object inside the value prop.
Note: the double curly braces
Lastly, pass the children in the Provider using props.children
Consume ThemeContext in our app
import React, { useContext } from 'react';
import { themeContext, ThemeProvider } from './themeContext';
const App = props => {
const { theme, toggleTheme } = useContext(themeContext);
return (
<ThemeProvider>
<section style={{
background: theme.background,
color: theme.primaryFont
}}
>
<button onClick={toggleTheme}> Toggle Theme </button>
</section>
</ThemeProvider>
)
}
In order to use our context, we have to utilize the useContext
hook from react.
Also, you must import the themeContext
in any file you want to access the theme context in.
However, the ThemeProvider
should just be placed at the highest level in your app.
We destructure our theme
and the toggleTheme
function from the themeContext
.
Now, you can apply the theme using the style
prop, styled-components
for custom components and any other methods you had in mind.
You can also call the toggleTheme
function to change the theme by clicking the button.
I believe your theme should be functional now.
If I missed anything or you have any questions, feel free to comment below.
I hope you are able to utilize theming now.