Create a themed favicon for color modes

There are 2 types of people in this world. Those who use dark mode and the other people.

I'm not sure about you, but I am basically a 100% user of dark mode on everything I do. There is nothing worse then sitting down at my desk to do some work or reading, opening a website and being hit with a solar flare from the bright white screen in front of me. However, this has caused me some issues when it comes to favicons on the web. A favicon is the small icon that you see in your browser tab that allows you to navigate the madness of your many tabs. If your like me and prefer to save your eyes from the blinding brightness of light mode, you may have noticed that some of these favicons are kind of hard to see.

The current favicon on my personal website

In this post we will go over how to toggle your favicon based on whether the user has their settings on dark mode or light mode.

Note: In this example I will be using a NextJS website, however this will work in any React application.

Lets start

Create our project

First thing we will do is create a new project.

npx create-next-app themeable-favicons

Create a custom hook

Next, we are going to create a custom hook called useSystemTheme. For our custom hook we are going to simply wrap an already created npm package. So lets install the package.

npm install react-use-system-theme

Now let's create a new file called useSystemTheme.js under a hooks directory. Our directory structure should look something like this.

│   └── components
│   └── hooks
|       ├── useSystemTheme.js
│   └── pages
|       ├── index.js

Inside our useSystemTheme.js file, we are going to import the useTheme function from our react-use-system-theme and return the value from it.

// useSystemTheme.js
import useTheme from 'react-use-system-theme';

export function useSystemTheme() {
  const theme = useTheme();
  return theme;
}

This is called "wrapping". The reason that we want to take this approach rather than just using the useTheme function directly is that if in the future, we decide to re-write this hook or use a different package, we will only need to make the change in one place.

Create our favicons

Next we want to create our favicons. I like to use RealFaviconGenerator.net as I think that it gives you the most robust options for your favicons.

Once you have created your two favicon packages (one for your dark mode and one for your light mode), you will want to rename the files in them from favicon.ico -> favicon-dark.ico etc.

│   └── public
|       ├── favicon-dark-16x16.png
|       ├── favicon-dark-32x32.png
|       ├── favicon-dark.ico
|       ├── favicon-light-16x16.png
|       ├── favicon-light-32x32.png
|       ├── favicon-light.ico

Use our hook to toggle favicons

Note: This step will be very specific to how NextJS works. However, you can do the same thing in any project that has a favicon.

By default, NextJS adds a favicon to our pages/index.js file. We will want to go in and remove this, as we are going to move the favicon to our pages/_app.js file.

Next, in our pages/_app.js file we will want to include our favicon. To do this, we are going to import the Head component from next/head. This will allow us to add to our html head tag.

Inside of our Head component, we will want to add our favicon that we removed from our pages/index.js.

import Head from "next/head";
import "../styles/globals.css";

function MyApp({ Component, pageProps }) {
  return (
    <>
      <Head>
        <link rel="icon" href={`/favicon-${theme}.ico`} />
      </Head>
      <Component {...pageProps} />
    </>
  );
}

export default MyApp;

Finally, we want to import and use our hook to toggle between the two favicons.

import Head from "next/head";
import { useSystemTheme } from "../hooks/useSystemTheme";
import "../styles/globals.css";

function MyApp({ Component, pageProps }) {
  const theme = useSystemTheme();
  return (
    <>
      <Head>
        <link rel="icon" href={`/favicon-${theme}.ico`} />
      </Head>
      <Component {...pageProps} />
    </>
  );
}

export default MyApp;

You will see in the code above that I am including the favicon with favicon-${theme}.ico. This will use the dark favicon that we created earlier on.

Now, when we switch our system theme from light mode, to the more appropriate dark mode, we will be able to toggle our favicon with it.

Some extra notes

  • While this technique works for system preferences, it can also be used with different color themes if you use something like Theme UI or Chakra UI.
  • You can find an example repo of this working here
  • You can find a working demo here
  • You can find this content in video form here