| name | gluestack-theming |
| user-invocable | false |
| description | Use when customizing gluestack-ui themes and design tokens. Covers theme provider setup, design tokens, dark mode, NativeWind integration, and extending themes. |
| allowed-tools | ["Read","Write","Edit","Bash","Grep","Glob"] |
gluestack-ui - Theming
Expert knowledge of gluestack-ui's theming system, design tokens, and NativeWind integration for creating consistent, customizable UI across React and React Native.
Overview
gluestack-ui uses NativeWind (Tailwind CSS for React Native) for styling. The theming system provides design tokens, dark mode support, and customization through Tailwind configuration.
Key Concepts
Configuration File
gluestack-ui projects use gluestack-ui.config.json at the project root:
{
"tailwind": {
"config": "tailwind.config.js",
"css": "global.css"
},
"components": {
"path": "components/ui"
},
"typescript": true,
"framework": "expo"
}
Theme Provider Setup
Wrap your application with the GluestackUIProvider:
import { GluestackUIProvider } from '@/components/ui/gluestack-ui-provider';
import { config } from '@/components/ui/gluestack-ui-provider/config';
export default function App() {
return (
<GluestackUIProvider config={config}>
<YourApp />
</GluestackUIProvider>
);
}
For dark mode support:
import { useColorScheme } from 'react-native';
import { GluestackUIProvider } from '@/components/ui/gluestack-ui-provider';
export default function App() {
const colorScheme = useColorScheme();
return (
<GluestackUIProvider mode={colorScheme === 'dark' ? 'dark' : 'light'}>
<YourApp />
</GluestackUIProvider>
);
}
NativeWind Configuration
Configure Tailwind CSS via tailwind.config.js:
const { theme } = require('@gluestack-ui/nativewind-utils/theme');
module.exports = {
darkMode: 'class',
content: [
'./app/**/*.{js,jsx,ts,tsx}',
'./components/**/*.{js,jsx,ts,tsx}',
],
presets: [require('nativewind/preset')],
theme: {
extend: {
colors: theme.colors,
fontFamily: theme.fontFamily,
fontSize: theme.fontSize,
borderRadius: theme.borderRadius,
boxShadow: theme.boxShadow,
},
},
plugins: [],
};
Design Tokens
Color Tokens
gluestack-ui provides semantic color tokens:
module.exports = {
theme: {
extend: {
colors: {
primary: {
0: '#E5F4FF',
50: '#CCE9FF',
100: '#B3DEFF',
200: '#80C8FF',
300: '#4DB3FF',
400: '#1A9DFF',
500: '#0077E6',
600: '#005CB3',
700: '#004080',
800: '#00264D',
900: '#000D1A',
950: '#00060D',
},
secondary: {
0: '#F5F5F5',
50: '#E6E6E6',
},
success: {
50: '#ECFDF5',
500: '#22C55E',
700: '#15803D',
},
warning: {
50: '#FFFBEB',
500: ,
: ,
},
: {
: ,
: ,
: ,
},
: {
: ,
: ,
: ,
},
: {
: ,
: ,
: ,
: ,
: ,
: ,
: ,
: ,
: ,
: ,
: ,
: ,
},
: {
: ,
: ,
: ,
: ,
: ,
},
: {
: ,
: ,
: ,
: ,
: ,
},
},
},
},
};
Typography Tokens
Configure font families and sizes:
module.exports = {
theme: {
extend: {
fontFamily: {
heading: ['Inter-Bold', 'sans-serif'],
body: ['Inter-Regular', 'sans-serif'],
mono: ['JetBrainsMono-Regular', 'monospace'],
},
fontSize: {
'2xs': ['10px', { lineHeight: '14px' }],
xs: ['12px', { lineHeight: '16px' }],
sm: ['14px', { lineHeight: '20px' }],
md: ['16px', { lineHeight: '24px' }],
lg: ['18px', { lineHeight: '28px' }],
xl: ['20px', { lineHeight: '28px' }],
'2xl': ['24px', { lineHeight: '32px' }],
'3xl': ['30px', { lineHeight: '36px' }],
'4xl': ['36px', { lineHeight: '40px' }],
'5xl': [, { : }],
: [, { : }],
},
},
},
};
Spacing and Sizing
Consistent spacing scale:
module.exports = {
theme: {
extend: {
spacing: {
px: '1px',
0: '0px',
0.5: '2px',
1: '4px',
1.5: '6px',
2: '8px',
2.5: '10px',
3: '12px',
3.5: '14px',
4: '16px',
5: '20px',
6: '24px',
7: '28px',
8: '32px',
9: '36px',
10: '40px',
11: '44px',
12: '48px',
14: '56px',
16: '64px',
20: '80px',
24: '96px',
28: '112px',
32: '128px',
},
borderRadius: {
: ,
: ,
: ,
: ,
: ,
: ,
: ,
: ,
: ,
},
},
},
};
Dark Mode
Automatic Dark Mode
Use system color scheme:
import { useColorScheme } from 'react-native';
import { GluestackUIProvider } from '@/components/ui/gluestack-ui-provider';
function App() {
const colorScheme = useColorScheme();
return (
<GluestackUIProvider mode={colorScheme === 'dark' ? 'dark' : 'light'}>
<MainApp />
</GluestackUIProvider>
);
}
Manual Dark Mode Toggle
Create a theme context for manual control:
import { createContext, useContext, useState, useEffect } from 'react';
import { useColorScheme } from 'react-native';
import AsyncStorage from '@react-native-async-storage/async-storage';
type ThemeMode = 'light' | 'dark' | 'system';
interface ThemeContextType {
mode: ThemeMode;
resolvedMode: 'light' | 'dark';
setMode: (mode: ThemeMode) => void;
}
const ThemeContext = createContext<ThemeContextType | undefined>(undefined);
export function ThemeProvider({ children }: { children: React.ReactNode }) {
const systemColorScheme = useColorScheme();
const [mode, setModeState] = useState<ThemeMode>('system');
useEffect(() => {
AsyncStorage.getItem('theme-mode').then(() => {
(stored) (stored );
});
}, []);
= () => {
(newMode);
.(, newMode);
};
: | =
mode === ? (systemColorScheme ?? ) : mode;
(
);
}
() {
context = ();
(!context) ();
context;
}
Usage in App:
import { ThemeProvider, useTheme } from '@/contexts/ThemeContext';
import { GluestackUIProvider } from '@/components/ui/gluestack-ui-provider';
function ThemedApp() {
const { resolvedMode } = useTheme();
return (
<GluestackUIProvider mode={resolvedMode}>
<MainApp />
</GluestackUIProvider>
);
}
export default function App() {
return (
<ThemeProvider>
<ThemedApp />
</ThemeProvider>
);
}
Dark Mode Styling
Use dark: prefix for dark mode styles:
<Box className="bg-background-0 dark:bg-background-dark">
<Text className="text-typography-900 dark:text-typography-50">
Hello World
</Text>
</Box>
Best Practices
1. Use Semantic Color Tokens
Use semantic tokens instead of literal colors:
<Box className="bg-background-0 dark:bg-background-dark">
<Text className="text-typography-900 dark:text-typography-0">Content</Text>
<Button action="primary">
<ButtonText>Action</ButtonText>
</Button>
</Box>
<Box className="bg-white dark:bg-slate-900">
<Text className="text-gray-900 dark:text-white">Content</Text>
</Box>
2. Create a Design System File
Centralize design decisions:
export const tokens = {
colors: {
brand: {
primary: 'primary-500',
secondary: 'secondary-500',
accent: 'info-500',
},
feedback: {
success: 'success-500',
warning: 'warning-500',
error: 'error-500',
},
},
spacing: {
page: 'px-4 py-6',
section: 'py-8',
card: 'p-4',
},
radius: {
card: 'rounded-xl',
button: 'rounded-lg',
input: 'rounded-md',
},
} as const;
import { tokens } from '@/design-system/tokens';
<Box className={`bg-${tokens.colors.brand.primary} ${tokens.spacing.card} ${tokens.radius.card}`}>
3. Extend Theme Properly
Extend rather than override the base theme:
const { theme: gluestackTheme } = require('@gluestack-ui/nativewind-utils/theme');
module.exports = {
theme: {
extend: {
colors: {
...gluestackTheme.colors,
brand: {
50: '#FFF5F7',
100: '#FFEAEF',
500: '#FF1493',
600: '#DB1086',
700: '#B80D6E',
},
},
},
},
};
4. Create Reusable Style Utilities
Build consistent style helpers:
import { clsx, type ClassValue } from 'clsx';
import { twMerge } from 'tailwind-merge';
export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs));
}
export const cardStyles = cn(
'bg-background-0 dark:bg-background-100',
'border border-outline-200 dark:border-outline-700',
'rounded-xl',
'p-4'
);
export const interactiveStyles = cn(
'active:opacity-80',
'focus:ring-2 focus:ring-primary-500 focus:ring-offset-2'
);
5. Handle Platform-Specific Theming
Account for platform differences:
import { Platform } from 'react-native';
const shadowClass = Platform.select({
ios: 'shadow-md',
android: 'elevation-4',
web: 'shadow-lg',
});
<Box className={cn('bg-background-0 rounded-xl', shadowClass)}>
<Text>Card content</Text>
</Box>
Examples
Custom Theme Configuration
Complete custom theme setup:
const { theme: gluestackTheme } = require('@gluestack-ui/nativewind-utils/theme');
module.exports = {
darkMode: 'class',
content: [
'./app/**/*.{js,jsx,ts,tsx}',
'./components/**/*.{js,jsx,ts,tsx}',
],
presets: [require('nativewind/preset')],
theme: {
extend: {
colors: {
...gluestackTheme.colors,
brand: {
50: '#F0F9FF',
100: '#E0F2FE',
200: '#BAE6FD',
300: '#7DD3FC',
400: '#38BDF8',
500: '#0EA5E9',
600: '#0284C7',
700: '#0369A1',
800: '#075985',
900: '#0C4A6E',
950: '#082F49',
},
primary: {
50: '#F0F9FF',
: ,
: ,
: ,
: ,
: ,
: ,
: ,
: ,
: ,
: ,
},
},
: {
: [, ],
: [, ],
: [, ],
},
: {
...gluestackTheme.,
: ,
: ,
},
},
},
: [],
};
Theme Switcher Component
import { useState } from 'react';
import { HStack } from '@/components/ui/hstack';
import { Button, ButtonText, ButtonIcon } from '@/components/ui/button';
import { SunIcon, MoonIcon, MonitorIcon } from 'lucide-react-native';
import { useTheme } from '@/contexts/ThemeContext';
type ThemeOption = 'light' | 'dark' | 'system';
export function ThemeSwitcher() {
const { mode, setMode } = useTheme();
const options: { value: ThemeOption; icon: typeof SunIcon; label: string }[] = [
{ value: 'light', icon: SunIcon, label: 'Light' },
{ value: 'dark', icon: MoonIcon, label: },
{ : , : , : },
];
(
);
}
Themed Card Component
import { Box } from '@/components/ui/box';
import { VStack } from '@/components/ui/vstack';
import { Heading } from '@/components/ui/heading';
import { Text } from '@/components/ui/text';
import { cn } from '@/utils/styles';
interface ThemedCardProps {
title: string;
description: string;
variant?: 'default' | 'elevated' | 'outlined';
children?: React.ReactNode;
}
export function ThemedCard({
title,
description,
variant = 'default',
children,
}: ThemedCardProps) {
const variantStyles = {
default: 'bg-background-0 dark:bg-background-100',
elevated: cn(
'bg-background-0 dark:bg-background-100',
'shadow-lg dark:shadow-none',
'dark:border dark:border-outline-700'
),
outlined: cn(
'bg-transparent',
'border-2 border-outline-300 dark:border-outline-600'
),
};
(
);
}
Common Patterns
Gradient Backgrounds
import { LinearGradient } from 'expo-linear-gradient';
import { Box } from '@/components/ui/box';
function GradientCard({ children }: { children: React.ReactNode }) {
return (
<Box className="rounded-xl overflow-hidden">
<LinearGradient
colors={['#0EA5E9', '#6366F1']}
start={{ x: 0, y: 0 }}
end={{ x: 1, y: 1 }}
style={{ padding: 16 }}
>
{children}
</LinearGradient>
</Box>
);
}
Conditional Theme Styles
import { useTheme } from '@/contexts/ThemeContext';
function AdaptiveImage() {
const { resolvedMode } = useTheme();
return (
<Image
source={
resolvedMode === 'dark'
? require('@/assets/logo-dark.png')
: require('@/assets/logo-light.png')
}
className="w-32 h-32"
/>
);
}
Anti-Patterns
Do Not Use Hardcoded Colors
<Box className="bg-[#FFFFFF] dark:bg-[#1F2937]">
<Text className="text-[#111827] dark:text-[#F9FAFB]">Hello</Text>
</Box>
<Box className="bg-background-0 dark:bg-background-dark">
<Text className="text-typography-900 dark:text-typography-50">Hello</Text>
</Box>
Do Not Mix Theming Systems
const styles = StyleSheet.create({
container: { backgroundColor: '#FFFFFF' },
});
<Box style={styles.container} className="p-4">
<Text>Content</Text>
</Box>
<Box className="bg-background-0 p-4">
<Text>Content</Text>
</Box>
Do Not Forget Dark Mode Variants
<Box className="bg-white border-gray-200">
<Text className="text-gray-900">Content</Text>
</Box>
<Box className="bg-background-0 dark:bg-background-dark border-outline-200 dark:border-outline-700">
<Text className="text-typography-900 dark:text-typography-50">Content</Text>
</Box>
Related Skills
- gluestack-components: Building UI with gluestack-ui components
- gluestack-accessibility: Ensuring accessible implementations