Getting started
Wrap your app with providers and add a theme preset picker.
1. Import the stylesheets
Add both imports at your app root — the preset file must come first:
@import "@codecanon/next-presets/default/nuteral.css";
@import "@codecanon/next-presets/styles.css";The first import sets the default :root variables (the appearance before any preset is chosen). The second loads all preset overrides. Swap nuteral.css for any other preset ID to change the default.
2. Wrap your app with providers
Add ThemeProvider and PresetProvider at the root. PresetProvider must be nested inside ThemeProvider:
import { ThemeProvider, PresetProvider } from "@codecanon/next-presets";
export default function RootLayout({
children,
}: {
children: React.ReactNode;
}) {
return (
<html lang="en" suppressHydrationWarning>
<body>
<ThemeProvider>
<PresetProvider>
{children}
</PresetProvider>
</ThemeProvider>
</body>
</html>
);
}Both providers persist their state to localStorage and restore it on the next page load.
3. Add a preset picker
First install the picker component into your project:
pnpm shadcn@latest add https://registry.codecanon.dev/r/preset-pickerThen use PresetPicker, PresetPickerContent, PresetPickerSearch, and PresetPickerList together to render a slide-out theme selector:
import {
PresetPicker,
PresetPickerTrigger,
PresetPickerContent,
PresetPickerSearch,
PresetPickerList,
PresetPickerThemeToggleGroup,
} from "@/components/preset-picker";
export function ThemeSidebar() {
return (
<PresetPicker>
<PresetPickerTrigger />
<PresetPickerContent>
<PresetPickerThemeToggleGroup />
<PresetPickerSearch />
<PresetPickerList />
</PresetPickerContent>
</PresetPicker>
);
}PresetPickerSearch handles text filtering and arrow-key / Enter navigation. PresetPickerList renders the filtered preset list with live mini-app previews.
4. Open the picker programmatically
Use usePresetPicker to toggle the sheet from any button or trigger inside a <PresetPicker>:
import {
PresetPicker,
PresetPickerContent,
PresetPickerSearch,
PresetPickerList,
PresetPickerTrigger,
PresetPickerThemeToggleGroup,
usePresetPicker,
} from "@/components/preset-picker";
function CustomOpenButton() {
const { toggleOpen } = usePresetPicker();
return <button onClick={toggleOpen}>Change Preset</button>;
}
export function App() {
return (
<PresetPicker>
<main className="my-app">
{/* Option A — built-in trigger button */}
<PresetPickerTrigger>Choose Preset</PresetPickerTrigger>
{/* Option B — custom trigger (see above) */}
<CustomOpenButton />
</main>
<PresetPickerContent>
<PresetPickerThemeToggleGroup />
<PresetPickerSearch />
<PresetPickerList />
</PresetPickerContent>
</PresetPicker>
);
}5. Read and set the preset in code
import { usePreset } from "@codecanon/next-presets";
function PresetControls() {
const { preset, setPreset, resetPreset } = usePreset();
return (
<div>
<p>Active: {preset ?? "none"}</p>
<button onClick={() => setPreset("violet-bloom")}>Violet Bloom</button>
<button onClick={() => setPreset("catppuccin")}>Catppuccin</button>
<button onClick={resetPreset}>Reset</button>
</div>
);
}6. Read and set the theme mode
import { useTheme } from "@codecanon/next-presets";
function ThemeToggle() {
const { isDarkTheme, setTheme } = useTheme();
return (
<button onClick={() => setTheme(isDarkTheme ? "light" : "dark")}>
{isDarkTheme ? "Switch to light" : "Switch to dark"}
</button>
);
}