useTheme and useThemeMode

Read and change the active theme.

Two hooks for the theme, covered in depth in Theming.

useTheme

Reads the active theme by name and sets it.

import { useTheme } from 'panelui-native';

const { theme, setTheme } = useTheme();
setTheme('moon-dark');
setTheme('system'); // follow the device
ValueTypeDescription
themePanelThemeActive theme name, e.g. 'dark' or 'grass'.
setTheme(name: ThemeName) => voidSwitch theme, or 'system'.

useThemeMode

Treats family and light/dark as separate axes, which is what a theme picker needs — a sun toggle has to stay inside the current family rather than jumping back to the default.

import { useThemeMode, PANEL_THEMES } from 'panelui-native';

const { family, mode, setFamily, setMode, toggleMode } = useThemeMode();

toggleMode();          // dark ↔ light, same family
setFamily('grass');    // switch family, same mode
ValueTypeDescription
familyPanelThemeFamilyActive family, with id, name and swatch colours.
mode'light' | 'dark'Mode within that family.
setFamily(id: string) => voidSwitch family, keeping the mode.
setMode(mode) => voidSet the mode, keeping the family.
toggleMode() => voidFlip the mode.

PANEL_THEMES lists every family with a display name and swatch, so a picker needs nothing hardcoded.

Examples

A sun/moon toggle

import { useThemeMode, Button, SunIcon, MoonIcon } from 'panelui-native';

function ThemeToggle() {
  const { mode, toggleMode } = useThemeMode();

  return (
    <Button
      size="icon"
      variant="ghost"
      onPress={toggleMode}
      accessibilityLabel={mode === 'dark' ? 'Use light theme' : 'Use dark theme'}
    >
      {mode === 'dark' ? <SunIcon size={18} /> : <MoonIcon size={18} />}
    </Button>
  );
}

toggleMode stays inside the current family, so a user on grass-dark lands on grass rather than the default light theme.

A family picker

import { useThemeMode, PANEL_THEMES } from 'panelui-native';

function FamilyPicker() {
  const { family, setFamily } = useThemeMode();

  return (
    <View className="flex-row gap-2">
      {PANEL_THEMES.map((t) => (
        <Pressable
          key={t.id}
          onPress={() => setFamily(t.id)}
          accessibilityRole="button"
          accessibilityState={{ selected: family.id === t.id }}
          className={cn(
            'h-10 w-10 rounded-full border-2',
            family.id === t.id ? 'border-ring' : 'border-transparent'
          )}
          style={{ backgroundColor: t.swatch }}
        />
      ))}
    </View>
  );
}

Following the device

const { setTheme } = useTheme();

<RadioGroup value={preference} onValueChange={(next) => {
  setPreference(next);
  setTheme(next as ThemeName);
}}>
  <RadioGroup.Item value="system" label="System" />
  <RadioGroup.Item value="light" label="Light" />
  <RadioGroup.Item value="dark" label="Dark" />
</RadioGroup>

Reading a token in JavaScript

Themes are CSS variables, so a colour you need as a value — for a chart, a status bar, a native navigator — comes from useCSSVariable, not from these hooks.

import { useCSSVariable } from 'uniwind';

const background = useCSSVariable('--color-background');

useToast

Covered on the Toast page.

On this page