惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

G
Google Developers Blog
S
Schneier on Security
The Hacker News
The Hacker News
P
Proofpoint News Feed
Spread Privacy
Spread Privacy
L
LINUX DO - 热门话题
L
Lohrmann on Cybersecurity
I
Intezer
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
www.infosecurity-magazine.com
www.infosecurity-magazine.com
Schneier on Security
Schneier on Security
Security Latest
Security Latest
AWS News Blog
AWS News Blog
B
Blog RSS Feed
Microsoft Security Blog
Microsoft Security Blog
有赞技术团队
有赞技术团队
博客园 - 叶小钗
The Last Watchdog
The Last Watchdog
O
OpenAI News
月光博客
月光博客
Hacker News: Ask HN
Hacker News: Ask HN
阮一峰的网络日志
阮一峰的网络日志
S
Security @ Cisco Blogs
Google Online Security Blog
Google Online Security Blog
云风的 BLOG
云风的 BLOG
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
Latest news
Latest news
P
Palo Alto Networks Blog
Last Week in AI
Last Week in AI
M
MIT News - Artificial intelligence
Google DeepMind News
Google DeepMind News
P
Proofpoint News Feed
C
CERT Recently Published Vulnerability Notes
Apple Machine Learning Research
Apple Machine Learning Research
U
Unit 42
PCI Perspectives
PCI Perspectives
博客园 - 聂微东
SecWiki News
SecWiki News
宝玉的分享
宝玉的分享
Forbes - Security
Forbes - Security
H
Heimdal Security Blog
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
Hugging Face - Blog
Hugging Face - Blog
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
T
Troy Hunt's Blog
博客园 - 三生石上(FineUI控件)
Application and Cybersecurity Blog
Application and Cybersecurity Blog
罗磊的独立博客
WordPress大学
WordPress大学
D
Darknet – Hacking Tools, Hacker News & Cyber Security

Maxime Heckel's Blog

On Rendering the Sky, Sunsets, and Planets - The Blog of Maxime Heckel Shades of Halftone - The Blog of Maxime Heckel Field Guide to TSL and WebGPU - The Blog of Maxime Heckel On Shaping Light: Real-Time Volumetric Lighting with Post-Processing and Raymarching for the Web - The Blog of Maxime Heckel Speaking at Figma Config 2025 - The Blog of Maxime Heckel Post-Processing Shaders as a Creative Medium - The Blog of Maxime Heckel On Crafting Painterly Shaders - The Blog of Maxime Heckel The Art of Dithering and Retro Shading for the Web - The Blog of Maxime Heckel Moebius-style post-processing and other stylized shaders - The Blog of Maxime Heckel Shining a light on Caustics with Shaders and React Three Fiber - The Blog of Maxime Heckel Real-time dreamy Cloudscapes with Volumetric Raymarching - The Blog of Maxime Heckel Painting with Math: A Gentle Study of Raymarching - The Blog of Maxime Heckel Building a magical AI-powered semantic search from scratch - The Blog of Maxime Heckel Beautiful and mind-bending effects with WebGL Render Targets - The Blog of Maxime Heckel Refraction, dispersion, and other shader light effects - The Blog of Maxime Heckel The magical world of Particles with React Three Fiber and Shaders - The Blog of Maxime Heckel The Study of Shaders with React Three Fiber - The Blog of Maxime Heckel Building a Design System from scratch - The Blog of Maxime Heckel Everything about Framer Motion layout animations - The Blog of Maxime Heckel Building a Vaporwave scene with Three.js - The Blog of Maxime Heckel Cubic Bézier: from math to motion - The Blog of Maxime Heckel First steps with GPT-3 for frontend developers - The Blog of Maxime Heckel Building the perfect GitHub CI workflow for your frontend team - The Blog of Maxime Heckel Migrating to Next.js - The Blog of Maxime Heckel Static Tweets with MDX and Next.js - The Blog of Maxime Heckel Advanced animation patterns with Framer Motion - The Blog of Maxime Heckel Scrollspy demystified - The Blog of Maxime Heckel The Power of Composition with CSS Variables - The Blog of Maxime Heckel My first failed SwiftUI project - The Blog of Maxime Heckel Guide to creating animations that spark joy with Framer Motion - The Blog of Maxime Heckel Using Shortcuts and serverless to build a personal Apple Health API - The Blog of Maxime Heckel SEO mistakes I've made and how I fixed them - The Blog of Maxime Heckel Going native: SwiftUI from the perspective of a React developer - The Blog of Maxime Heckel Build your own preview deployment service - The Blog of Maxime Heckel The little guide to CI/CD for frontend developers - The Blog of Maxime Heckel Immigrating to the US - The Blog of Maxime Heckel The physics behind spring animations - The Blog of Maxime Heckel Generate screenshots of your code with a serverless function - The Blog of Maxime Heckel How to use Framer Motion with Emotion styled-components - The Blog of Maxime Heckel Data Fetching with NextJS: What I learned - The Blog of Maxime Heckel Learning in public - The Blog of Maxime Heckel Fixing the dark mode flash issue on server rendered websites - The Blog of Maxime Heckel How to fix NPM link duplicate dependencies issues - The Blog of Maxime Heckel Running scheduled cross-browser end-to-end tests on Github CI - The Blog of Maxime Heckel How I built my first custom ESLint rule - The Blog of Maxime Heckel React Lazy: a take on preloading views - The Blog of Maxime Heckel Automated UI accessibility testing with Cypress - The Blog of Maxime Heckel Building a GraphQL wrapper for the Docker API - The Blog of Maxime Heckel Getting started with Typescript on Gatsby - The Blog of Maxime Heckel Rebuilding Redux with Hooks and Context - The Blog of Maxime Heckel Asynchronous rendering with React - The Blog of Maxime Heckel Using Flow generics to type generic React components - The Blog of Maxime Heckel How to efficiently type your styled-components with Flow - The Blog of Maxime Heckel How I got started with Kubernetes on GKE - The Blog of Maxime Heckel React sub-components Part 3: Whitelisting sub-components with flow - The Blog of Maxime Heckel React sub-components Part 2: Using the new Context API - The Blog of Maxime Heckel React sub-components - The Blog of Maxime Heckel Running Golang tests with Jest - The Blog of Maxime Heckel No title No title
Switching off the lights - Adding dark mode to your React app - The Blog of Maxime Heckel
Maxime Heckel · 2019-03-06 · via Maxime Heckel's Blog

Since the release of macOS Mojave, a lot of people have expressed their love for dark mode and a lot of websites like Twitter, Reddit or Youtube have followed this trend. Why you may ask? I think the following quote from this Reddit post summarizes it pretty well:

Night is dark. Screen is bright. Eyes hurt. 
Night is dark. Screen is dark. Eyes not hurt.

As I want to see even more websites have this feature, I started experimenting with an easy a non-intrusive way to add a dark mode to my React projects, and this is what this article is about. 
In this post, I’m going to share with you how I built dark mode support for a sample React app with Emotion themes. We’ll use a combination of contexts, hooks, and themes to build this feature and the resulting implementation should not cause any fundamental changes to the app.

Note: I use Emotion as a preference, but you can obviously use CSS modules or even inlines styles to implement a similar feature.

What we’re going to build:

The objective here is to have a functional dark mode on a website with the following features:

  • a switch to be able to enable or disable the dark mode

  • some local storage support to know on load if the dark mode is activated or not

  • a dark and light theme for our styled components to consume

Theme definitions

The first thing we will need for our dark mode is a definition of what it stands for color-wise. Emotion themes are very well adapted to do this. Indeed we can define all our dark mode colors and light mode colors in distinct files for example and have these colors use the same keys to be accessed. Below we can see an example of a theme I’m using in one of my projects and its dark equivalent.

The theme definitions for our example

2

const black = "#161617";

15

const theme = mode => (mode === 'dark' ? themeDark : themeLight);

You’ll notice in the code above that I gave very descriptive names to my variables such as background or body. I always try to make sure none of the variables names are based on the color so I can use the same name across the different themes I’m using.

Now that we have both our dark and light theme, we can focus on how we’re going to consume these themes.

Theme Provider

This is the core component of this post. The Theme Provider will contain all the logic for our dark mode feature: the toggle function, which theme to load when your site renders the first time, and also, inject the theme to all your child components.
With the help of React Hooks and Context, it is possible with just a few lines of code and without the need to build any classes or HoC (Higher order Components).

Loading the state in Context

First, we need to define a default state for our Theme Provider. The two elements that define these states are:

  • a boolean that tells us whether or not the dark theme is activated, defaults to false.

  • a function toggle that will be defined later.

This state will be the default state in a ThemeContext, because we want to have access to these items across our all application. In order to avoid having to wrap any page of our app in a ThemeContext.Consumer, we’ll build a custom useTheme hook based on the useContext hook. Why hooks? I think this tweet summarizes it pretty well:

As it is stated in the tweet above, I really believe that hooks are more readable than render props:

Default state and ThemeContext

1

const defaultContextData = {

6

const ThemeContext = React.createContext(defaultContextData);

7

const useTheme = () => React.useContext(ThemeContext);

In this ThemeProvider component, we’ll inject both the correct theme and the toggle function to the whole app. Additionally, it will contain the logic to load the proper theme when rendering the app. That logic will be contained within a custom hook: useEffectDarkMode.

Code for the useEffectDarkMode custom hook

1

const useEffectDarkMode = () => {

2

const [themeState, setThemeState] = React.useState({

4

hasThemeMounted: false,

7

React.useEffect(() => {

8

const lsDark = localStorage.getItem('dark') === 'true';

9

setThemeState({ ...themeState, dark: lsDark, hasThemeMounted: true });

12

return [themeState, setThemeState];

In the code above, we take advantage of both the useState and useEffect hook. The useEffectDarkMode Hook will set a local state, which is our theme state when mounting the app. Notice that we pass an empty array [] as the second argument of the useEffect hook. Doing this makes sure that we only call this useEffect when the ThemeProvider component mounts (otherwise it would be called on every render of ThemeProvider).

Code for the ThemeProvider component that provides both theme and themeState to the whole application

1

import { ThemeProvider as EmotionThemeProvider } from 'emotion-theming';

2

import React, { Dispatch, ReactNode, SetStateAction } from 'react';

3

import theme from './theme';

5

const defaultContextData = {

10

const ThemeContext = React.createContext(defaultContextData);

11

const useTheme = () => React.useContext(ThemeContext);

13

const useEffectDarkMode = () => {

14

const [themeState, setThemeState] = React.useState({

16

hasThemeLoaded: false,

18

React.useEffect(() => {

19

const lsDark = localStorage.getItem('dark') === 'true';

20

setThemeState({ ...themeState, dark: lsDark, hasThemeLoaded: true });

23

return [themeState, setThemeState];

26

const ThemeProvider = ({ children }: { children: ReactNode }) => {

27

const [themeState, setThemeState] = useEffectDarkMode();

29

if (!themeState.hasThemeLoaded) {

39

const theme = themeState.dark ? theme('dark') : theme('light');

41

const toggle = () => {

46

<EmotionThemeProvider theme={theme}>

47

<ThemeContext.Provider

49

dark: themeState.dark,

54

</ThemeContext.Provider>

55

</EmotionThemeProvider>

59

export { ThemeProvider, useTheme };

The code snippet above contains the (almost) full implementation of our ThemeProvider:

  • If dark is set to true in localStorage, we update the state to reflect this and the theme that will be passed to our Emotion Theme Provider will be the dark one. As a result, all our styled component using this theme will render in dark mode.

  • Else, we’ll keep the default state which means that the app will render in light mode.

The only missing piece in our implementation is the toggle function. Based on our use case, it will have to do the following things:

  • reverse the theme and update the themeState

  • update the dark key in the localStorage

Code for the toggle function

2

const dark = !themeState.dark;

3

localStorage.setItem('dark', JSON.stringify(dark));

4

setThemeState({ ...themeState, dark });

This function is injected in the ThemeContext and aims to toggle between light and dark mode.

Adding the theme switcher

In the previous part, we’ve implemented all the logic and components needed, now it’s time to use them on our app!
Since we’ve based our implementation on React Context, we can simply import the ThemeProvider and wrap our application within it.

The next step is to provide a button on the UI to enable or disable the dark mode. Luckily, we have access to all the things we need to do so through the useTheme hook, which will give us access to what we’ve passed to our ThemeContext.Provider in part two of this post.

Sample app wrapped in the ThemeProvider using the useTheme hook

1

import React from 'react';

2

import styled from '@emotion/styled';

3

import { useTheme } from './ThemeContext';

5

const Wrapper = styled('div')`

6

background: ${(props) => props.theme.background};

10

color: ${(props) => props.theme.body};

15

const themeState = useState();

19

<h1>Dark Mode example</h1>

21

<button onClick={() => themeState.toggle()}>

22

{themeState.dark ? 'Switch to Light Mode' : 'Switch to Dark Mode'}

Considering we’re in the default state (light mode), clicking this button will call the toggle function provided through the ThemeContext which will set the local storage variable dark to true and the themeState dark variable to true. This will switch the theme that is passed in the Emotion Theme Provider from light to dark. As a result, all our styled components using that theme will end up using the dark theme, and thus our entire application is now in dark mode. 
In the example above, the Wrapper component uses the colors of the theme for the fonts and the background, when switching from light to dark these CSS properties will change and hence the background will go from gray to black and the font from black to white.

Conclusion

We successfully added support for dark mode in our React application without having done any fundamental changes! I really hope this post will inspire others to add this feature to their own website or app in order to make them more easy on the eye when used during night time.
Moreover, this kind of feature is a great example of hook implementations and how to use the latest features of React to build amazing things.
I got this feature on my own website/portfolio and this is how it looks:

Gif showcasing switching from light to dark mode

The dark mode implementation on my website (sorry for the low frame rate 😅).

If you want to get a sample project with dark mode to hack on top of it, check out this minimal React app I built with all the code showcased on this article.