Skip to content

ctx.theme stable

The theme domain — read the merged set of presets (core Dark/Light plus everything registered via ctx.registerThemePreset), switch the active theme, and manage the user's custom themes. The built-in theme picker and theme editor consume the domain through here rather than reaching into app state.

ts
ctx.theme: ThemeService

Example

tsx
// read reactively (e.g. via React's useSyncExternalStore)
const state = ctx.theme.getState();
state.presets; // core Dark/Light + every registered preset
state.activeId; // the active theme's id
state.customThemes; // the user's saved custom themes

// switch the active theme (a preset id or a custom theme id)
ctx.theme.setActive("gruvbox-dark");

// resolve an id to its base + effective vars (for a swatch / preview)
const { base, vars } = ctx.theme.resolve(state.activeId);

// observe changes (active theme, custom themes, or the set of presets)
const sub = ctx.theme.subscribe((s) => render(s));
sub.dispose();

Methods

ThemeService (ctx.theme):

MethodWhat it does
getState()Current frozen ThemeState.
subscribe(listener)Observe theme-state changes; returns a Disposable.
setActive(id)Switch the active theme by id (preset or custom).
resolve(id)Resolve an id to a ResolvedTheme (base + vars).
saveCustom(theme)Persist a custom theme to disk and reload the list.
deleteCustom(id)Delete a custom theme from disk and reload.
reloadCustom()Reload custom themes from disk.
exportTheme(theme)Strip the id for sharing/serialization.
importTheme(data)Validate JSON into a CustomTheme (fresh id).

Types

ThemeService · ThemeState · ThemePreset · ResolvedTheme · CustomTheme · ThemeVars.

Notes

Presets are dynamic — part of the reactive state, not a static list, because extensions register and unregister them at runtime. Contributing a preset is a separate, registration-time concern: ctx.registerThemePreset. Core ships only Dark and Light as the always-present fallback; the bundled presets (Tokyo Night, Solarized Light, Gruvbox Dark) live in the theme-presets built-in extension, so the picker's bundled set is itself just contributions.