Letter F

How to Add a Dynamic Favicon in Next.js (Based on User Theme)

Fortune Zviregei
5 min read
#Next.js#Favicon#Web Development
Learn how to add a dynamic favicon to your Next.js site that adapts to light and dark mode using the prefers-color-scheme media query. I walk through a real-world use case from a church website I built, showing exactly how to generate, organize, and implement separate favicons for each theme without any extra JavaScript. Clean, simple, and theme-aware.

Introduction

I was building a fresh website for my local church group recently. Naturally, I went with Next.js. Between the free hosting on Vercel, the clean Hero UI components, and what I genuinely believe is the best CMS available, Payload CMS, it just made sense. Chef’s kiss 🤌.

As I moved through the build, I hit a small but annoying detail. The favicon I used looked decent in light mode, but in dark mode it lost contrast and became practically invisible. That led me to a simple but effective solution: dynamic favicons based on the user's theme.

TLDR

  • Generate two favicons, one for light mode and one for dark mode. Real Favicon Generator worked great for me.
  • Place them somewhere like /public/images/seo/. Generally anywhere within the public directory will do. Just take note of where you put them
  • Dynamically assign them using prefers-color-scheme in the metadata export in your layout.(jsx | tsx) file. See the code snippet below

Assumptions

You're already familiar with Next.js, using the App Router, and you’ve got theme toggle logic in place. If you're still setting up Hero UI, check their installation docs.

Step-by-Step Setup

1. Generate the Icons

Go to Real Favicon Generator and create two sets. One optimized for light backgrounds, the other for dark. I renamed mine to favicon-light.svg and favicon-dark.svg so I wouldn’t mix them up.

2. Organize Your Files

I dropped both icons in the /public/images/seo/ folder, but use whatever directory you like as long as its within the public directory.

3. Add to the Metadata

In your layout.tsx (or .js), where you export the metadata, add the following:

Next.js meta config
1export const metadata = {
2  title: "My Website",
3  icons: [
4    {
5      rel: "icon",
6      url: "/images/seo/favicon-light.svg",
7      media: "(prefers-color-scheme: light)",
8    },
9    {
10      rel: "icon",
11      url: "/images/seo/favicon-dark.svg",
12      media: "(prefers-color-scheme: dark)",
13    },
14  ],
15};

The media attribute checks whether the user prefers a light or dark theme, then loads the right icon. You get automatic theme awareness without touching extra JavaScript.

Final Thoughts

It's a small change, but it makes your site feel much more thoughtful. Especially when building for a group like a church or community project, that level of detail shows care.

If this helped, you might enjoy some of the other things I’m building:

Share this post

Related Posts

Cooking Software

Introduction to Software Design Patterns

Design patterns are reusable solutions to common problems in software design. Think of them like templates or recipes that help you write cleaner, more maintainable code. They’re not rigid rules but proven strategies used by developers for decades. Whether you're coding an app from scratch or maintaining a massive codebase, patterns help make your code more understandable, flexible, and scalable.

New!