Dynamic theme
A React provider for dynamic theming — user-selected colors, scoped palettes per section, light/dark mode switching, and image-based color extraction.
Install
1. Add the Theme component
npx shadcn@latest add https://material-shadcn.vercel.app/r/themeAdds components/theme.tsx and installs material-shadcn automatically.
2. Wrap your app
import { Theme } from "@/components/theme"
export default function RootLayout({ children }) {
return (
<html lang="en">
<body>
<Theme seed="#6750A4">
{children}
</Theme>
</body>
</html>
)
}The root <Theme> applies CSS variables to <html>, manages light/dark mode, and persists user preferences to localStorage.
3. Control the theme
import { useTheme } from "@/components/theme"
function ThemePicker() {
const { seed, setSeed, cycleColorMode } = useTheme()
return (
<div>
<input
type="color"
value={seed}
onChange={(e) => setSeed(e.target.value)}
/>
<button onClick={cycleColorMode}>
Toggle dark mode
</button>
</div>
)
}Scoped themes
Nest <Theme> inside a parent <Theme> to scope a different palette to any section. It renders a <div> with CSS variables as inline styles — all child components pick them up via cascade.
import { Theme } from "@/components/theme"
import { Variant } from "material-shadcn"
function ProductCard({ imageColor }: { imageColor: string }) {
return (
<Theme seed={imageColor} variant={Variant.CONTENT}>
<div className="bg-card text-card-foreground p-4 rounded-lg">
<h3 className="text-primary">Product Title</h3>
<button className="bg-primary text-primary-foreground">
Buy now
</button>
</div>
</Theme>
)
}You can pass className, style, and any other div props to the nested <Theme>.
Image-based seeding
Pass an HTMLImageElement to setSeed() or as the seed prop to extract a dominant color and generate a theme from it.
function ImageTheme() {
const { setSeed } = useTheme()
return (
<img
src="/hero.jpg"
onLoad={(e) => setSeed(e.currentTarget)}
alt="Hero"
/>
)
}Color mode
The root <Theme> manages light/dark mode with three states: system, light, and dark.
function ColorModeToggle() {
const { colorMode, setColorMode, cycleColorMode } = useTheme()
return (
<div>
{/* Cycle through system → light → dark */}
<button onClick={cycleColorMode}>
{colorMode}
</button>
{/* Or set directly */}
<button onClick={() => setColorMode('dark')}>
Dark mode
</button>
</div>
)
}Persistence
By default, seed color, variant, and color mode are saved to localStorage under the key material-shadcn-theme. Pass storageKey={null} to disable persistence.
{/* Custom storage key */}
<Theme storageKey="my-app-theme">
{children}
</Theme>
{/* Disable persistence */}
<Theme storageKey={null}>
{children}
</Theme>Next: See Reference for all props and hook return values.