Theres no place like home!

Preferred Color Scheme in React

Eric Bishard
September 29th, 2019 · 2 min read

I have a demo that, on the fly, is able to swap the users theme from light to dark. It also remembers the persons last selected choice by saving that selection of 'dark' or 'light' to localStorage and checking there first when setting the theme on the next user visit.

This is great, but users of iOS, Windows 10, Android and such have an option to set their preferred theme. Here is how I can do that in my Windows 10 machine.

How to Read This Value in CSS (media queries)

I was reading this post here on the Dev Community about “Two Media Queries You Should Care About” and it talks about how to use the prefers-color-scheme media query:

1@media (prefers-color-scheme: dark) {
2 body {
3 background: #111;
4 color: #eee;
5 }
6}

And this got me thinking. I already have the work done in my application to swap themes on the fly by user input (a switch) or by their last preferred selection that I have stored in localStorage.

But before I look for their preferred theme in localStorage or in conjunction with that, I should be querying about the preferred color scheme of their device. Since I only know how to do this in CSS, shown above, I don’t want to write something super hacky in JS. I found this article: How to detect a user’s preferred color scheme in JavaScript which gives me a great idea of how to do this with React Hooks.

How to Read This Value in JS with a React Hook

In the above article, a section called “Reactive JS Approach” gave me an even better idea of using JavaScript’s watchMedia() method. This is great, but I already use a React Hook in my project that wraps the watchMedia() method and exposes it as a React Hook.

This library is called: react-media-hook and can be used like so:

1import { useMediaPredicate } from "react-media-hook";
2let breakpoint = useMediaPredicate("(min-width: 600px)") ? "medium" : "small";

In fact, that is exactly how I am using it for watching my 'small' vs 'medium' breakpoint. But instead of passing in a min-width query, I can pass in a prefers-color-scheme query instead.

1const preferredTheme = useMediaPredicate("(prefers-color-scheme: dark)") ? "dark" : "light";

This gives me an easy way to know if they prefer 'dark' vs 'light' on their device. I can keep this value as a string or boolean value and now I can easily determine this in my app with just a few lines of JS.

Below is an initial first stab at setting my theme. I use React ContextAPI and Hooks to set this value globally:

1import React, { useState, useEffect, createContext } from 'react';
2import { useMediaPredicate } from "react-media-hook";
3
4const AppContext = createContext();
5
6const AppProvider = props => {
7 const preferredTheme = useMediaPredicate('(prefers-color-scheme: dark)') ? 'dark' : 'light'
8 const [appData, setApp] = useState({
9
10 navOpen: false,
11 toggleSidenav: value => setApp(data => (
12 { ...data, navOpen: value }
13 )),
14
15 themeMode: localStorage.getItem(''kr-todo-theme') || preferredTheme,
16 changeTheme: mode => setApp(data => (
17 {...data, themeMode: mode }
18 )),
19
20 });
21
22 useEffect(() => {
23 localStorage.setItem(''kr-todo-theme', appData.themeMode)
24 }, [appData.themeMode]
25 );
26
27 return <AppContext.Provider value={appData}>{props.children}</AppContext.Provider>
28}
29
30export { AppContext, AppProvider };

To show this working, I can simulate the user hitting my app for the first time by removing their setting in localStorage. This will force my code to check the preferred theme, and base it’s initial theme setting to 'dark' if they prefer that, otherwise 'light'.

Also, it will remember my last saved theme preference. So I like the idea of using the prefers-color-scheme as an indication as to what I should use so long as there are not user settings telling me they prefer otherwise.

I hope you enjoyed this article and if you would like to see the full demo application working with Kendo UI Sass theme Builder and KendoReact Components, you can get that here: GitHub.com/httpJunkie/kr-todo-hooks. It’s the same application I used as a demo at ReactLiveNL in Amsterdam.

I also have an exhaustive article on how to work with React Hooks. I go over State and Effects, Context, Reducers, Custom Hooks and Managing Control State of Components.

More articles from Eric S Bishard

Redirect with React Router and Hooks

This guide provides background on React, state management, and serve as an exhaustive guide to the built in react hooks.

April 26th, 2019 · 2 min read

The Developers Guide To React Amsterdam

Attending the React Summit 2020 event, mark your calendars, its April 16th and 17th.

April 12th, 2019 · 4 min read
Made with 💧 😅 & 😂's in the East Bay
Link to $https://twitter.com/httpjunkieLink to $https://github.com/httpjunkieLink to $https://www.linkedin.com/in/eric-b/Link to $https://stackoverflow.com/users/2623804/eric-bishardLink to $https://medium.com/@httpjunkie