Advanced Usage
Advanced configuration and usage patterns
Custom Themes
You can define custom themes beyond light and dark:
<ThemeProvider themes={["light", "dark", "purple", "blue", "green"]}>
{children}
</ThemeProvider>Use the value prop to map theme names to different attribute values:
<ThemeProvider
themes={["light", "dark", "purple"]}
value={{
light: "light-mode",
dark: "dark-mode",
purple: "purple-mode",
}}
attribute="data-theme"
>
{children}
</ThemeProvider>Multiple Attributes
You can apply themes to multiple attributes simultaneously:
<ThemeProvider attribute={["class", "data-theme"]}>{children}</ThemeProvider>This is useful when you need both class-based styling (for Tailwind) and data attributes (for custom CSS).
Forced Themes
Force a specific theme for a page or section:
<ThemeProvider forcedTheme="dark">{children}</ThemeProvider>When forcedTheme is set, it overrides user preferences and localStorage. This is useful for:
- Landing pages that should always be light
- Documentation sections that should always be dark
- A/B testing different themes
Disable System Theme
Disable system theme detection:
<ThemeProvider enableSystem={false} defaultTheme="light">
{children}
</ThemeProvider>When enableSystem is false, the "system" option is removed from available themes.
Storage Options
By default, Better Themes uses localStorage to persist theme preferences across all tabs. You can switch to sessionStorage for per-tab theme isolation:
<ThemeProvider storage="sessionStorage">{children}</ThemeProvider>localStorage vs sessionStorage
| Storage Type | Behavior |
|---|---|
localStorage (default) | Shared across all tabs |
sessionStorage | Isolated per tab |
When to use sessionStorage
Use sessionStorage when you need different themes in different tabs. For example, in a multi-user environment where multiple users are logged into different accounts in separate tabs, each user can have their own theme preference.
Custom Storage Key
Change the key used to store theme preferences:
<ThemeProvider storageKey="my-app-theme">{children}</ThemeProvider>Useful when you have multiple theme providers in the same app or want to avoid conflicts with other libraries.
Disable Color Scheme
Disable the color-scheme CSS property:
<ThemeProvider enableColorScheme={false}>{children}</ThemeProvider>The color-scheme property tells browsers which color scheme to use for native UI elements. You might want to disable it if you're handling this manually.
Disable Transitions
Disable CSS transitions when switching themes:
<ThemeProvider disableTransitionOnChange>{children}</ThemeProvider>This prevents flashy transitions when themes change. Better Themes temporarily disables all transitions, applies the theme, then re-enables them.
Content Security Policy (CSP)
If you're using CSP headers, provide a nonce for the inline script:
<ThemeProvider nonce="your-nonce-here">{children}</ThemeProvider>The nonce is passed to the inline script that prevents flash on load.
Programmatic Theme Control
Control themes programmatically:
function () {
const { , , , } = ();
// Set theme directly
("dark");
// Set theme with callback
(() => ( === "dark" ? "light" : "dark"));
// Access all available themes
.(); // ["light", "dark", "system"]
// Access system preference
.(); // "dark" or "light"
}Conditional Rendering
Safely render theme-dependent content:
import { useTheme } from "better-themes";
import { useEffect, useState } from "react";
function ThemedContent() {
const [mounted, setMounted] = useState(false);
const { theme } = useTheme();
useEffect(() => {
setMounted(true);
}, []);
if (!mounted) {
return <div>Loading...</div>;
}
return (
<div>
{theme === "dark" && <DarkContent />}
{theme === "light" && <LightContent />}
</div>
);
}TypeScript
Better Themes is fully typed. Import types for your own components:
import type { ThemeProviderProps, UseThemeProps } from "better-themes";
function CustomProvider(props: ThemeProviderProps) {
// ...
}
function useCustomTheme(): UseThemeProps {
// ...
}