A lightweight React theme provider with system theme detection
npm install @ionatan21/react-theme-providerprefers-color-scheme preference
bash
npm install @ionatan21/react-theme-provider
`
`bash
yarn add @ionatan21/react-theme-provider
`
`bash
pnpm add @ionatan21/react-theme-provider
`
Basic Usage
$3
`tsx
import { ThemeProvider } from '@ionatan21/react-theme-provider';
function App() {
return (
);
}
`
That's it! The library includes default styles for common HTML elements and 19 CSS variables that are applied automatically.
$3
`tsx
import { useTheme } from '@ionatan21/react-theme-provider';
function ThemeToggle() {
const { theme, setTheme } = useTheme();
return (
);
}
`
$3
`css
.my-component {
background-color: hsl(var(--background));
color: hsl(var(--foreground));
border: 1px solid hsl(var(--border));
}
.my-button {
background-color: hsl(var(--primary));
color: hsl(var(--primary-foreground));
}
`
API
$3
Main component that should wrap your application.
#### Props
| Prop | Type | Default | Description |
|------|------|---------|-------------|
| children | ReactNode | - | Child components |
| defaultTheme | 'light' \| 'dark' \| 'system' | 'system' | Default theme if none is saved |
| storageKey | string | 'theme' | Key for localStorage |
| lightColors | Partial | - | Custom colors for light theme |
| darkColors | Partial | - | Custom colors for dark theme |
| disableDefaultColors | boolean | false | Disables default CSS variables |
| disableBaseStyles | boolean | false | Disables base styles for HTML elements |
$3
Hook to access and modify the current theme.
#### Returns
`typescript
{
theme: 'light' | 'dark' | 'system'; // User's selected theme
resolvedTheme: 'light' | 'dark'; // Effective theme applied
setTheme: (theme: Theme) => void; // Function to change theme
}
`
- theme: The theme selected by the user (can be 'system')
- resolvedTheme: The actual theme applied to the DOM (always 'light' or 'dark')
- setTheme: Function to change and persist the theme
Default Styles
The library includes automatic styles for common HTML elements. You can use them directly without writing additional CSS.
$3
`css
--background / Main background /
--foreground / Main text /
--card / Card background /
--card-foreground / Card text /
--popover / Popover background /
--popover-foreground / Popover text /
--primary / Primary color /
--primary-foreground / Text on primary /
--secondary / Secondary color /
--secondary-foreground / Text on secondary /
--muted / Muted color /
--muted-foreground / Muted text /
--accent / Accent color /
--accent-foreground / Text on accent /
--destructive / Destructive/error color /
--destructive-foreground / Text on destructive /
--border / Border color /
--input / Input color /
--ring / Focus ring color /
`
$3
- body - Background and text color
- button - Complete styles with variants
- input, textarea, select - Form styles
- a - Links with hover
- h1-h6 - Headings
- table, th, td - Tables
- code, pre - Code blocks
- blockquote, hr - Content elements
- Custom scrollbar
$3
`html
`
$3
You can customize colors while keeping the base styles:
`tsx
lightColors={{
primary: '200 100% 50%',
background: '0 0% 98%'
}}
darkColors={{
primary: '200 100% 60%',
background: '220 20% 8%'
}}
>
`
Note: Colors are in HSL format without hsl(): 'hue saturation% lightness%'
$3
If you prefer to use only the theme switching functionality without the styles:
`tsx
`
Or disable everything completely:
`tsx
`
SSR / Next.js
$3
`tsx
// app/providers.tsx
'use client';
import { ThemeProvider } from '@ionatan21/react-theme-provider';
export function Providers({ children }: { children: React.ReactNode }) {
return (
{children}
);
}
`
`tsx
// app/layout.tsx
import { Providers } from './providers';
export default function RootLayout({ children }) {
return (
{children}
);
}
`
Important: Add suppressHydrationWarning to the element to avoid hydration warnings when the theme is applied.
$3
`tsx
// pages/_app.tsx
import { ThemeProvider } from '@ionatan21/react-theme-provider';
import type { AppProps } from 'next/app';
export default function App({ Component, pageProps }: AppProps) {
return (
);
}
`
Real Example
`tsx
import { ThemeProvider, useTheme } from '@ionatan21/react-theme-provider';
import { Moon, Sun, Monitor } from 'lucide-react';
function App() {
return (
My Application
With full support for light and dark themes
);
}
function Header() {
const { theme, setTheme } = useTheme();
return (
);
}
function ThemeSelector() {
const { theme, setTheme } = useTheme();
const themes = [
{ value: 'light', icon: Sun, label: 'Light' },
{ value: 'dark', icon: Moon, label: 'Dark' },
{ value: 'system', icon: Monitor, label: 'System' },
] as const;
return (
{themes.map(({ value, icon: Icon, label }) => (
key={value}
onClick={() => setTheme(value)}
className={theme === value ? 'active' : ''}
aria-label={Switch to ${label} theme}
>
{label}
))}
);
}
export default App;
`
`css
/ styles.css /
/ Additional customization example /
.my-card {
background-color: hsl(var(--card));
color: hsl(var(--card-foreground));
border: 1px solid hsl(var(--border));
border-radius: 0.5rem;
padding: 1.5rem;
}
.my-card h2 {
color: hsl(var(--primary));
margin-bottom: 1rem;
}
.alert-error {
background-color: hsl(var(--destructive) / 0.1);
color: hsl(var(--destructive));
border: 1px solid hsl(var(--destructive));
padding: 1rem;
border-radius: 0.375rem;
}
``