Introduction
Components
- Accordion
- Action Sheet
- Alert
- Audio Player
- Audio Recorder
- Audio Waveform
- Avatar
- Badge
- BottomSheet
- Button
- Camera
- Camera Preview
- Card
- Carousel
- Checkbox
- Collapsible
- Color Picker
- Combobox
- Date Picker
- File Picker
- Gallery
- Hello Wave
- Icon
- Image
- Input
- Input OTP
- Link
- MediaPicker
- Mode Toggle
- Onboarding
- ParallaxScrollView
- Picker
- Popover
- Progress
- Radio
- ScrollView
- SearchBar
- Separator
- Share
- Sheet
- Skeleton
- Spinner
- Switch
- Table
- Tabs
- Text
- Toast
- Toggle
- Video
- View
Charts
Installation Methods
Option 1: CLI Installation (Recommended)
The fastest way to set up BNA UI in your Expo project:
pnpm dlx bna-ui init
This command will automatically:
- Install required dependencies
- Configure your tsconfig.json
- Set up the theme system
- Create necessary hooks and utilities
Option 2: Manual Installation
If you prefer to set up BNA UI manually, follow these steps:
1. Install Dependencies
pnpm dlx expo install @react-navigation/native expo-router
2. Update tsconfig.json
Replace your existing tsconfig.json
with the following configuration:
{
"extends": "expo/tsconfig.base",
"compilerOptions": {
"strict": true,
"paths": {
"@/*": ["./*"]
}
},
"include": [
"**/*.ts",
"**/*.tsx",
".expo/types/**/*.ts",
"expo-env.d.ts",
"docs/contents/audio-waveform.mdx"
]
}
3. Create Theme System
Create a theme
folder in your project root with the following files:
theme/colors.ts
const lightColors = {
// Base colors
background: '#FFFFFF',
foreground: '#000000',
// Card colors
card: '#F2F2F7',
cardForeground: '#000000',
// Popover colors
popover: '#F2F2F7',
popoverForeground: '#000000',
// Primary colors
primary: '#18181b',
primaryForeground: '#FFFFFF',
// Secondary colors
secondary: '#F2F2F7',
secondaryForeground: '#18181b',
// Muted colors
muted: '#78788033',
mutedForeground: '#71717a',
// Accent colors
accent: '#F2F2F7',
accentForeground: '#18181b',
// Destructive colors
destructive: '#ef4444',
destructiveForeground: '#FFFFFF',
// Border and input
border: '#C6C6C8',
input: '#e4e4e7',
ring: '#a1a1aa',
// Text colors
text: '#000000',
textMuted: '#71717a',
// Legacy support for existing components
tint: '#18181b',
icon: '#71717a',
tabIconDefault: '#71717a',
tabIconSelected: '#18181b',
// Default buttons, links, Send button, selected tabs
blue: '#007AFF',
// Success states, FaceTime buttons, completed tasks
green: '#34C759',
// Delete buttons, error states, critical alerts
red: '#FF3B30',
// VoiceOver highlights, warning states
orange: '#FF9500',
// Notes app accent, Reminders highlights
yellow: '#FFCC00',
// Pink accent color for various UI elements
pink: '#FF2D92',
// Purple accent for creative apps and features
purple: '#AF52DE',
// Teal accent for communication features
teal: '#5AC8FA',
// Indigo accent for system features
indigo: '#5856D6',
};
const darkColors = {
// Base colors
background: '#000000',
foreground: '#FFFFFF',
// Card colors
card: '#1C1C1E',
cardForeground: '#FFFFFF',
// Popover colors
popover: '#18181b',
popoverForeground: '#FFFFFF',
// Primary colors
primary: '#e4e4e7',
primaryForeground: '#18181b',
// Secondary colors
secondary: '#1C1C1E',
secondaryForeground: '#FFFFFF',
// Muted colors
muted: '#78788033',
mutedForeground: '#a1a1aa',
// Accent colors
accent: '#1C1C1E',
accentForeground: '#FFFFFF',
// Destructive colors
destructive: '#dc2626',
destructiveForeground: '#FFFFFF',
// Border and input - using alpha values for better blending
border: '#38383A',
input: 'rgba(255, 255, 255, 0.15)',
ring: '#71717a',
// Text colors
text: '#FFFFFF',
textMuted: '#a1a1aa',
// Legacy support for existing components
tint: '#FFFFFF',
icon: '#a1a1aa',
tabIconDefault: '#a1a1aa',
tabIconSelected: '#FFFFFF',
// Default buttons, links, Send button, selected tabs
blue: '#0A84FF',
// Success states, FaceTime buttons, completed tasks
green: '#30D158',
// Delete buttons, error states, critical alerts
red: '#FF453A',
// VoiceOver highlights, warning states
orange: '#FF9F0A',
// Notes app accent, Reminders highlights
yellow: '#FFD60A',
// Pink accent color for various UI elements
pink: '#FF375F',
// Purple accent for creative apps and features
purple: '#BF5AF2',
// Teal accent for communication features
teal: '#64D2FF',
// Indigo accent for system features
indigo: '#5E5CE6',
};
export const Colors = {
light: lightColors,
dark: darkColors,
};
// Export individual color schemes for easier access
export { darkColors, lightColors };
// Utility type for color keys
export type ColorKeys = keyof typeof lightColors;
theme/globals.ts
export const HEIGHT = 48;
export const FONT_SIZE = 17;
export const BORDER_RADIUS = 26;
export const CORNERS = 999;
theme/theme-provider.tsx
import {
DarkTheme,
DefaultTheme,
ThemeProvider as RNThemeProvider,
} from '@react-navigation/native';
import 'react-native-reanimated';
import { useColorScheme } from '@/hooks/useColorScheme';
import { Colors } from '@/theme/colors';
type Props = {
children: React.ReactNode;
};
export const ThemeProvider = ({ children }: Props) => {
const colorScheme = useColorScheme();
// Create custom themes that use your Colors
const customLightTheme = {
...DefaultTheme,
colors: {
...DefaultTheme.colors,
primary: Colors.light.primary,
background: Colors.light.background,
card: Colors.light.card,
text: Colors.light.text,
border: Colors.light.border,
notification: Colors.light.red,
},
};
const customDarkTheme = {
...DarkTheme,
colors: {
...DarkTheme.colors,
primary: Colors.dark.primary,
background: Colors.dark.background,
card: Colors.dark.card,
text: Colors.dark.text,
border: Colors.dark.border,
notification: Colors.dark.red,
},
};
return (
<RNThemeProvider
value={colorScheme === 'dark' ? customDarkTheme : customLightTheme}
>
{children}
</RNThemeProvider>
);
};
4. Create Hooks
Create a hooks
folder in your project root with the following files:
hooks/useColorScheme.ts
export { useColorScheme } from 'react-native';
hooks/useColorScheme.web.ts
import { useEffect, useState } from 'react';
import { useColorScheme as useRNColorScheme } from 'react-native';
/**
* To support static rendering, this value needs to be re-calculated on the client side for web
*/
export function useColorScheme() {
const [hasHydrated, setHasHydrated] = useState(false);
useEffect(() => {
setHasHydrated(true);
}, []);
const colorScheme = useRNColorScheme();
if (hasHydrated) {
return colorScheme;
}
return 'light';
}
hooks/useModeToggle.tsx
import { useColorScheme } from '@/hooks/useColorScheme';
import { useState } from 'react';
import { Appearance, ColorSchemeName } from 'react-native';
type Mode = 'light' | 'dark' | 'system';
interface UseModeToggleReturn {
isDark: boolean;
mode: Mode;
setMode: (mode: Mode) => void;
currentMode: ColorSchemeName;
toggleMode: () => void;
}
export function useModeToggle(): UseModeToggleReturn {
const [mode, setModeState] = useState<Mode>('system');
const colorScheme = useColorScheme();
const isDark = colorScheme === 'dark';
const toggleMode = () => {
switch (mode) {
case 'light':
setMode('dark');
break;
case 'dark':
setMode('system');
break;
case 'system':
setMode('light');
break;
}
};
const setMode = (newMode: Mode) => {
setModeState(newMode);
if (newMode === 'system') {
Appearance.setColorScheme(null); // Reset to system default
} else {
Appearance.setColorScheme(newMode);
}
};
return {
isDark,
mode,
setMode,
currentMode: colorScheme,
toggleMode,
};
}
hooks/useThemeColor.ts
/**
* Learn more about light and dark modes:
* https://docs.expo.dev/guides/color-schemes/
*/
import { useColorScheme } from '@/hooks/useColorScheme';
import { Colors } from '@/theme/colors';
export function useThemeColor(
props: { light?: string; dark?: string },
colorName: keyof typeof Colors.light & keyof typeof Colors.dark
) {
const theme = useColorScheme() ?? 'light';
const colorFromProps = props[theme];
if (colorFromProps) {
return colorFromProps;
} else {
return Colors[theme][colorName];
}
}
4. Update package.json
"main": "expo-router/entry",
Next Steps
After completing the installation, you can:
- Wrap your app with the ThemeProvider in your root component (App.tsx or _layout.tsx):
import { Stack } from 'expo-router';
import { StatusBar } from 'expo-status-bar';
import { ThemeProvider } from '@/theme/theme-provider';
export default function RootLayout() {
return (
<ThemeProvider>
<Stack>
<Stack.Screen name='(tabs)' options={{ headerShown: false }} />
<Stack.Screen name='+not-found' />
</Stack>
<StatusBar style='auto' />
</ThemeProvider>
);
}
- Start using BNA UI components in your project
- Customize the theme by modifying the colors and globals files to match your brand
Features
- π¨ Comprehensive theming system with light and dark mode support
- π§ Type-safe color utilities with full TypeScript support
- π― iOS-style design tokens following Apple's Human Interface Guidelines
- π Cross-platform compatibility (iOS, Android, Web)
- β‘ Performance optimized with proper hydration handling for web
- π Flexible mode switching (light, dark, system)
Support
For additional help and documentation, visit our [documentation site] or check out our [GitHub repository].
Happy building with BNA UI! π
On This Page
Installation MethodsOption 1: CLI Installation (Recommended)Option 2: Manual Installation1. Install Dependencies2. Update tsconfig.json3. Create Theme Systemtheme/colors.tstheme/globals.tstheme/theme-provider.tsx4. Create Hookshooks/useColorScheme.tshooks/useColorScheme.web.tshooks/useModeToggle.tsxhooks/useThemeColor.ts4. Update package.jsonNext StepsFeaturesSupport