2024's Update: just use Next themes
In this quick article, I will be implementing dark mode to a quick React app with a custom React hook,
Before we start, let's see how is this supposed to work in three easy steps:
- We will check first if the chosen theme isn't stored on localStorage
- If it exists, we will simply set it as the default theme
- Otherwise, we will have to use the light theme as the default one
So let's get started!
Let's start by setting up the custom hook first, we will be using useEffect & useState imported from React, we will check localStorage when the component mounts for the first time, then we'll follow the second step!
import { useEffect, useState } from 'react'export default () => {const [theme, setTheme] = useState('light')useEffect(() => {const localTheme = window.localStorage.getItem('components/theme')if (localTheme) {setTheme(localTheme)}}, [])}
import { useEffect, useState } from 'react'export default () => {const [theme, setTheme] = useState('light')useEffect(() => {const localTheme = window.localStorage.getItem('components/theme')if (localTheme) {setTheme(localTheme)}}, [])}
Let's setup a function now that toggles in between themes and stores the selected theme in localStorage
import { useEffect, useState } from 'react'export default () => {const [theme, setTheme] = useState('light')const toggleTheme = () => {if (theme === 'light') {window.localStorage.setItem('theme', 'dark')setTheme('dark')} else {window.localStorage.setItem('theme', 'light')setTheme('light')}}useEffect(() => {const localTheme = localStorage.getItem('components/theme')if (localTheme) {setTheme(localTheme)}}, [])}
import { useEffect, useState } from 'react'export default () => {const [theme, setTheme] = useState('light')const toggleTheme = () => {if (theme === 'light') {window.localStorage.setItem('theme', 'dark')setTheme('dark')} else {window.localStorage.setItem('theme', 'light')setTheme('light')}}useEffect(() => {const localTheme = localStorage.getItem('components/theme')if (localTheme) {setTheme(localTheme)}}, [])}
All good now, we only need to return the theme value along with the function so we can access it when we import the custom hook!
import { useEffect, useState } from 'react'export default () => {const [theme, setTheme] = useState('light')const toggleTheme = () => {if (theme === 'light') {window.localStorage.setItem('theme', 'dark')setTheme('dark')} else {window.localStorage.setItem('theme', 'light')setTheme('light')}}useEffect(() => {const localTheme = window.localStorage.getItem('components/theme')if (localTheme) {setTheme(localTheme)}}, [])return [theme, toggleTheme]}
import { useEffect, useState } from 'react'export default () => {const [theme, setTheme] = useState('light')const toggleTheme = () => {if (theme === 'light') {window.localStorage.setItem('theme', 'dark')setTheme('dark')} else {window.localStorage.setItem('theme', 'light')setTheme('light')}}useEffect(() => {const localTheme = window.localStorage.getItem('components/theme')if (localTheme) {setTheme(localTheme)}}, [])return [theme, toggleTheme]}
Finally, let's see how we can use it when we import it
import React from 'react'import useDarkMode from './useDarkMode'export default () => {const [theme, toggleTheme] = useDarkMode()return (<divstyle={{background: theme === 'dark' ? '#000' : '#fff',color: theme === 'dark' ? '#fff' : '#000',}}><button type="button" onClick={toggleTheme}>Switch theme</button></div>)}
import React from 'react'import useDarkMode from './useDarkMode'export default () => {const [theme, toggleTheme] = useDarkMode()return (<divstyle={{background: theme === 'dark' ? '#000' : '#fff',color: theme === 'dark' ? '#fff' : '#000',}}><button type="button" onClick={toggleTheme}>Switch theme</button></div>)}
You can find the full example here