Theming
Use built-in themes, switch at runtime, or compose your own theme from CSS tokens.
Wit Grid ships a shadcn-style, CSS-variable theming system: eight built-in themes, runtime switching with no
flash of the wrong theme, and full custom theme composition. All theme interaction goes through GridApi — the
same handle you get from onGridReady or useGridApi(). There's no separate theming object to construct.
Built-in themes
dark
Default. High-contrast dark surface tuned for long sessions.
light
Neutral light surface for bright environments.
light-hc
High-contrast light theme for accessibility.
dark-hc
High-contrast dark theme for accessibility.
cool-blue
Modern tech aesthetic with a blue accent.
warm-orange
Warm accent theme.
minimal-monochrome
Greyscale theme with no accent color.
spreadsheet
Excel/Google Sheets-style daylight theme.
Setting a theme
Set a built-in theme name declaratively via initialState. It's resolved once, atomically, before the grid's
first paint — no flash of the default theme.
<Grid initialState={{ themeName: 'light' }} />Switching themes at runtime
Get api from onGridReady (React) or the return value of createClientGrid / createInfiniteGrid /
createServerPageGrid (core), then call the theme methods directly on it:
api.switchTheme('dark');
api.switchTheme('cool-blue');
const theme = api.getTheme();
const unsubscribe = api.onThemeChange((theme) => {
console.log('Theme changed:', theme);
});Composing a custom theme
ThemeTokens is a plain flat object — a themed variant of a built-in theme is just a spread, no helper function
required:
import { getBuiltInTheme, type ThemeTokens } from '@witbybit/wit-grid-core';
export const acmeTheme: ThemeTokens = {
...getBuiltInTheme('light'),
bgColor: '#1a1a2e',
textColor: '#eaeaea',
focusRing: '#00d4ff',
headerBg: '#0f0f1e',
headerText: '#b0b0b0',
};Apply it declaratively (atomic, no flicker) or imperatively:
<Grid initialState={{ themeOverrides: acmeTheme }} />api.setTheme(acmeTheme);Incremental tweaks
For small changes on top of whatever theme is currently active, use api.mergeTheme({ focusRing: '#10b981' })
instead of setTheme — it merges into the active theme rather than replacing it.
System color scheme detection
ThemeManager.detectSystemPreference() is a standalone static helper — use it to pick the right built-in theme
name up front:
import { ThemeManager } from '@witbybit/wit-grid-core';
const prefersDark = ThemeManager.detectSystemPreference();
<Grid initialState={{ themeName: prefersDark ? 'dark' : 'light' }} />To keep the grid in sync as the OS preference changes live:
useEffect(() => {
const mql = window.matchMedia('(prefers-color-scheme: dark)');
const onChange = (e: MediaQueryListEvent) => api.switchTheme(e.matches ? 'dark' : 'light');
mql.addEventListener('change', onChange);
return () => mql.removeEventListener('change', onChange);
}, [api]);Bridging into host CSS
Use themeToCSSVariables when you want to expose grid theme tokens as CSS custom properties on your own
stylesheet — useful for styling a toolbar or footer to match the active grid theme:
import { BUILT_IN_THEMES, getBuiltInTheme, themeToCSSVariables } from '@witbybit/wit-grid-react';
const styleVars = themeToCSSVariables(getBuiltInTheme('dark'));See the full theming guide in the repository
for the complete ThemeTokens token reference, merging semantics, and CSS variable customization.
Last updated on