Managing the user interface theme is crucial for enhancing user experience. With the increasing adoption of dark and light modes, providing users with the flexibility to choose their preferred theme has become more important. In this blog post, I’ll walk you through how to detect the system theme, allow users to set their preferred theme (auto, dark, or light), and save their selection using localStorage in a React application with Fluent UI.
Thank me by sharing on Twitter 🙏
Introduction
Themes play a vital role in the look and feel of applications. Users often have preferences for either a light or dark theme, and some prefer to follow the system’s default theme. Using Fluent UI, a popular React component library developed by Microsoft, we can easily implement theme switching in our React applications. In this post, I’ll show you how to:
- Detect the system’s theme.
- Save and retrieve the user’s theme preference.
- Apply the selected theme to your application.
- Allow users to switch between themes.
Detecting the System Theme
The first step is to detect whether the user prefers a light or dark theme based on their system settings. This can be done using the window.matchMedia API, which allows us to query the system’s color scheme.
const LIGHT = 'light';
const DARK = 'dark';
const getSystemTheme = () => {
return window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches ? DARK : LIGHT;
};Here, I define constants for light and dark themes. The getSystemTheme function uses window.matchMedia to check if the system prefers a dark color scheme and returns the appropriate theme.
Saving and Retrieving User’s Preference
Next, we’ll need to save the user’s theme preference in localStorage so that it persists across sessions. We’ll also retrieve this preference when the application loads.
Metapen Pencil A8 for Apple iPad 2018-2025, (2X Faster Charge), Tilt Sensitivity, Pixel Precision, Stylus Pen for iPad 11/10/9/8/7/6th Gen, Pro 12.9/11/13-inch M4, Air 3/4/5/M2/M3, Mini 5/6th, White
$14.99 (as of November 27, 2025 01:42 GMT +00:00 - More infoProduct prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on [relevant Amazon Site(s), as applicable] at the time of purchase will apply to the purchase of this product.)Empire of AI: Dreams and Nightmares in Sam Altman's OpenAI
$18.81 (as of November 26, 2025 18:35 GMT +00:00 - More infoProduct prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on [relevant Amazon Site(s), as applicable] at the time of purchase will apply to the purchase of this product.)Rosalina's Storybook
$27.15 (as of November 26, 2025 18:35 GMT +00:00 - More infoProduct prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on [relevant Amazon Site(s), as applicable] at the time of purchase will apply to the purchase of this product.)const AUTO = 'auto';
const THEME_KEY = 'user-theme';
const getStoredTheme = () => {
return localStorage.getItem(THEME_KEY) || AUTO;
};
const storeTheme = (theme: string) => {
localStorage.setItem(THEME_KEY, theme);
};The AUTO constant represents the automatic theme mode, where the application follows the system theme. The getStoredTheme function retrieves the stored theme from localStorage, defaulting to auto if no preference is found. The storeTheme function saves the user’s preference in localStorage.
Applying the Selected Theme
With the ability to detect the system theme and manage user preferences, we now need to apply the selected theme to our application. Fluent UI provides a ThemeProvider component that allows us to switch between themes easily.
import { createTheme, ThemeProvider, webLightTheme, webDarkTheme } from '@fluentui/react-components';
import React, { useEffect, useState } from 'react';
const getCurrentTheme = (storedTheme: string) => {
if (storedTheme === AUTO) {
return getSystemTheme();
}
return storedTheme;
};
export const useTheme = () => {
const [theme, setTheme] = useState<string>(getCurrentTheme(getStoredTheme()));
useEffect(() => {
const handleSystemThemeChange = (e: MediaQueryListEvent) => {
if (getStoredTheme() === AUTO) {
setTheme(e.matches ? DARK : LIGHT);
}
};
const systemThemeMediaQuery = window.matchMedia('(prefers-color-scheme: dark)');
systemThemeMediaQuery.addEventListener('change', handleSystemThemeChange);
return () => {
systemThemeMediaQuery.removeEventListener('change', handleSystemThemeChange);
};
}, []);
const updateTheme = (newTheme: string) => {
storeTheme(newTheme);
setTheme(getCurrentTheme(newTheme));
};
return { theme, updateTheme };
};
const App = ({ children }) => {
const { theme, updateTheme } = useTheme();
const appliedTheme = theme === DARK ? webDarkTheme : webLightTheme;
return (
<ThemeProvider theme={appliedTheme}>
{children}
<button onClick={() => updateTheme(LIGHT)}>Light</button>
<button onClick={() => updateTheme(DARK)}>Dark</button>
<button onClick={() => updateTheme(AUTO)}>Auto</button>
</ThemeProvider>
);
};
export default App;In this code snippet, we create a custom useTheme hook that manages the theme state. The getCurrentTheme function determines the theme based on the user’s preference or the system theme if auto is selected. The useTheme hook also listens for changes in the system theme when auto is selected.
The App component uses the useTheme hook to apply the selected theme using Fluent UI’s ThemeProvider. It also provides buttons for users to switch between light, dark, and auto modes.
Allowing Users to Switch Themes
Finally, let’s provide users with a way to switch between themes. We’ve already added buttons in the App component for this purpose. When a user clicks a button, the updateTheme function is called, updating the theme state and storing the new preference in localStorage.
<button onClick={() => updateTheme(LIGHT)}>Light</button>
<button onClick={() => updateTheme(DARK)}>Dark</button>
<button onClick={() => updateTheme(AUTO)}>Auto</button>These buttons allow users to switch between light, dark, and auto themes easily. The updateTheme function updates the theme state and stores the user’s preference.
Conclusion
In this blog post, we’ve learned how to detect the system’s theme, save and retrieve the user’s theme preference, apply the selected theme using Fluent UI, and allow users to switch between themes. By following these steps, you can enhance the user experience of your React application by providing a seamless and customizable theme management system.
Implementing theme management in your application not only improves user satisfaction but also makes your application more accessible and user-friendly. Whether users prefer a light or dark theme, or want to follow the system’s default, you now have the tools to provide them with the best experience possible.


