Recently I've been working with a UI that has a white background, and honestly, it's burning my eyes something terrible.
Lots of other users with all sorts of different situations will really appreciate an application that automatically adjusts to their preference. Users may be viewing your app late at night in a dark setting, trying to preserve battery life or wanting better contrast when there is glare on a screen.
So here's my implementation of a react hook allowing me to efficiently manage the dark and light theme of my UI.
Your eyes will thank you!
This useIsDarkMode
hook is an efficient, reusable way to detect and respond to the user’s preferred colour scheme in a React application. Its design makes it performance-friendly and safe to use, particularly with TypeScript’s added type safety. This hook simplifies theme detection by returning either true or false to my components that wish to use it.
import { useEffect, useState } from 'react'
export const useIsDarkMode = (): boolean => {
const getCurrentScheme = (): boolean =>
window.matchMedia('(prefers-color-scheme: dark)').matches
const [isDarkMode, setIsDarkMode] = useState<boolean>(getCurrentScheme)
useEffect(() => {
const mediaQuery = window.matchMedia('(prefers-color-scheme: dark)')
const handleChange = (e: MediaQueryListEvent) =>
setIsDarkMode(e.matches)
mediaQuery.addEventListener('change', handleChange)
return () => mediaQuery.removeEventListener('change', handleChange)
}, [])
return isDarkMode
}
getCurrentScheme
is a helper function that queries the window.matchMedia
API to determine if the user’s preferred colour scheme is dark. The window.matchMedia('(prefers-color-scheme: dark)')
check will return an object whose matches
property will be true
if dark mode is preferred, otherwise false
.
The useState
hook, which expects a boolean value, initialises isDarkMode
with the result of getCurrentScheme
, allowing the hook to synchronise with the user’s current colour scheme.
A useEffect
is used to determine when this component mounts, and the mediaQuery
is defined to represent the prefers-color-scheme: dark
media query.
An Event Listener is set up for handleChange
which listens for changes in the colour scheme and updates isDarkMode
using setIsDarkMode
whenever a change is detected. The MediaQueryListEvent
type is used in the handleChange
function’s parameter, ensuring that TypeScript knows the e.matches
property will be available on the event object. This type safety prevents potential errors if e
were incorrectly typed.
By returning a cleanup function that removes the event listener, the hook avoids memory leaks if the component using this hook is unmounted.
Finally, the hook returns the isDarkMode
state so that any component using this hook can access the dark mode status. By encapsulating this logic in a custom hook, any component in the application can easily check if dark mode is active without having to duplicate the logic. This separation promotes clean and maintainable code.
Example Usage of useIsDarkMode
Here’s a simple example of how to use the useIsDarkMode
hook within a React component:
import React from 'react'
import { useIsDarkMode } from './useIsDarkMode'
export const ThemeIndicator: React.FC = () => {
const isDarkMode = useIsDarkMode()
return (
<div style={{
backgroundColor: isDarkMode ? '#333' : '#FFF',
color: isDarkMode ? '#FFF' : '#333',
}}>
<p>{isDarkMode ? 'Dark' : 'Light'} Mode is Enabled</p>
</div>
)
}