Theming Guide
The theme system is a shape-agnostic named-slot registry. You store whatever your app needs — no enforced structure, no default colors, no palette generation. You own the shape entirely.
Installation
Section titled “Installation”import { registerTheme, useTheme, setTheme, getTheme, useThemeName, generateSpaceUnits } from 'react-native-small-ui/theme';Registering a Theme
Section titled “Registering a Theme”Default slot (unnamed)
Section titled “Default slot (unnamed)”Registers and activates immediately as 'default':
import { registerTheme } from 'react-native-small-ui/theme';
registerTheme({ light: { primary: '#007AFF', background: '#fff', text: '#000' }, dark: { primary: '#0A84FF', background: '#000', text: '#fff' },});Do this once at app initialization — before any component renders.
Named slot
Section titled “Named slot”Registers without switching the active theme:
registerTheme('ocean', { light: { primary: '#0af', background: '#f0faff' }, dark: { primary: '#08c', background: '#001a33' },});
registerTheme('warm', { light: { primary: '#f80', background: '#fffaf0' }, dark: { primary: '#c60', background: '#1a0d00' },});Switching Themes
Section titled “Switching Themes”import { setTheme } from 'react-native-small-ui/theme';
setTheme('ocean'); // returns truesetTheme('missing'); // throws: theme "missing" not found. Available: default, ocean, warmsetTheme always throws on unknown names — catches configuration mistakes at runtime regardless of environment.
Accessing the Theme
Section titled “Accessing the Theme”In React — useTheme
Section titled “In React — useTheme”Returns unknown. Cast to your own type:
import { useTheme } from 'react-native-small-ui/theme';
type AppTheme = { light: { primary: string; background: string; text: string }; dark: { primary: string; background: string; text: string };};
function ThemedButton() { const theme = useTheme() as AppTheme;
return ( <Button _light={{ backgroundColor: theme.light.primary }} _dark={{ backgroundColor: theme.dark.primary }} /> );}With a selector
Section titled “With a selector”// Typed slice — avoids the full cast at call siteconst primary = useTheme((t) => (t as AppTheme).light.primary);The selector re-renders only when the selected value changes.
Outside React — getTheme
Section titled “Outside React — getTheme”import { getTheme } from 'react-native-small-ui/theme';
const theme = getTheme() as AppTheme;Active theme name — useThemeName
Section titled “Active theme name — useThemeName”import { useThemeName } from 'react-native-small-ui/theme';
const name = useThemeName(); // 'default' | 'ocean' | ...TypeScript Pattern
Section titled “TypeScript Pattern”Define your theme type once and reuse it:
export type AppTheme = { light: { primary: string; background: string; text: string; border: string; }; dark: { primary: string; background: string; text: string; border: string; };};
// Register at app initimport { registerTheme } from 'react-native-small-ui/theme';import type { AppTheme } from './theme';
const myTheme: AppTheme = { light: { primary: '#007AFF', background: '#fff', text: '#000', border: '#ddd' }, dark: { primary: '#0A84FF', background: '#000', text: '#fff', border: '#333' },};
registerTheme(myTheme);// In components — one import, full inferenceimport { useTheme } from 'react-native-small-ui/theme';import type { AppTheme } from './theme';
const primary = useTheme((t) => (t as AppTheme).light.primary);Runtime Theme Switching
Section titled “Runtime Theme Switching”import { registerTheme, setTheme, useThemeName } from 'react-native-small-ui/theme';
// Register all themes at startupregisterTheme('blue', { light: { primary: '#007AFF' }, dark: { primary: '#0A84FF' },});registerTheme('green', { light: { primary: '#34C759' }, dark: { primary: '#30D158' },});
function ThemeSwitcher() { const name = useThemeName();
return ( <View> <Text>Active: {name}</Text> <TouchableOpacity onPress={() => setTheme('blue')}> <Text>Blue</Text> </TouchableOpacity> <TouchableOpacity onPress={() => setTheme('green')}> <Text>Green</Text> </TouchableOpacity> </View> );}Spacing Units
Section titled “Spacing Units”The theme system is shape-agnostic — your spacing scale is just a plain object like any other token. Define it however fits your design system.
Plain object (recommended starting point)
Section titled “Plain object (recommended starting point)”No utility function required. A plain object is the simplest and most readable approach:
export const space = { xs: 4, sm: 8, md: 16, lg: 24, xl: 32,};
export const radius = { sm: 4, md: 8, lg: 16, full: 9999,};Use it directly in createComponent or as props:
import { space, radius } from './tokens';
const Card = createComponent(View, { padding: space.md, borderRadius: radius.md, gap: space.sm,});
// Or as props at the call site<Card padding={space.lg} />Store it inside your theme object to keep everything in one place:
registerTheme({ colors: { primary: '#8b59a0', background: '#fff' }, space: { xs: 4, sm: 8, md: 16, lg: 24, xl: 32 }, radius: { sm: 4, md: 8, lg: 16, full: 9999 },});
// Access via useThemeconst theme = useTheme() as AppTheme;<Card padding={theme.space.md} borderRadius={theme.radius.md} />generateSpaceUnits — for systematic scales
Section titled “generateSpaceUnits — for systematic scales”When you want a proportional scale generated from a base unit (e.g. 4px grid), generateSpaceUnits saves repetitive arithmetic:
import { generateSpaceUnits } from 'react-native-small-ui/theme';
const space = generateSpaceUnits(4);// { '.25': 1, '.50': 2, '.75': 3, '1': 4, '2': 8, '3': 12, ..., '10': 40 }
const space8 = generateSpaceUnits(8, { maxAmount: 20, withNegatives: true });// { '1': 8, '-1': -8, '2': 16, '-2': -16, ... }Use the plain object approach when your scale is small and stable. Use generateSpaceUnits when you need a larger, mathematically consistent scale and want to avoid typing it by hand.
Using with createComponent
Section titled “Using with createComponent”import { createComponent } from 'react-native-small-ui';import { useTheme } from 'react-native-small-ui/theme';import { TouchableOpacity, Text } from 'react-native';import type { AppTheme } from './theme';
// Create outside renderconst Button = createComponent(TouchableOpacity, { borderRadius: 8, alignItems: 'center',});
function ThemedButton({ children }: { children: React.ReactNode }) { const theme = useTheme() as AppTheme;
return ( <Button padding={16} _light={{ backgroundColor: theme.light.primary }} _dark={{ backgroundColor: theme.dark.primary }} > <Text>{children}</Text> </Button> );}Do I need the theme system?
Section titled “Do I need the theme system?”No. Hard-coded colors work fine with core-only:
import { createComponent } from 'react-native-small-ui';
const Button = createComponent(TouchableOpacity, { backgroundColor: '#007AFF', _dark: { backgroundColor: '#0A84FF' },});Can I use my own design token structure?
Section titled “Can I use my own design token structure?”Yes — the store accepts any shape. Define whatever structure fits your design system:
registerTheme({ colors: { brand: '#007AFF', danger: '#FF3B30' }, radii: { sm: 4, md: 8, lg: 16 }, fontSizes: { body: 16, heading: 24 },});Does the theme enforce light/dark keys?
Section titled “Does the theme enforce light/dark keys?”No. The shape is unknown. The light/dark convention shown in examples is just that — a convention. Store what works for your app.
Next Steps
Section titled “Next Steps”- Color Utilities - Color manipulation tools
- Bundle Optimization - Minimize bundle size
- Hooks Reference - Full hooks reference