0
# Theming
1
2
React Navigation's theming system provides built-in light and dark themes with full customization support. Themes control colors, fonts, and visual styling across all navigation components.
3
4
## Capabilities
5
6
### Built-in Themes
7
8
Pre-configured light and dark themes that follow platform design guidelines.
9
10
```typescript { .api }
11
/** Default light theme following iOS design guidelines */
12
const DefaultTheme: Theme;
13
14
/** Default dark theme following iOS design guidelines */
15
const DarkTheme: Theme;
16
17
interface Theme {
18
/** Whether this is a dark theme */
19
dark: boolean;
20
/** Color palette for navigation components */
21
colors: {
22
/** Primary color for active elements */
23
primary: string;
24
/** Background color for screens */
25
background: string;
26
/** Background color for cards and surfaces */
27
card: string;
28
/** Primary text color */
29
text: string;
30
/** Border and separator color */
31
border: string;
32
/** Color for notifications and badges */
33
notification: string;
34
};
35
/** Font configurations for different text weights */
36
fonts: {
37
regular: FontStyle;
38
medium: FontStyle;
39
bold: FontStyle;
40
heavy: FontStyle;
41
};
42
}
43
44
interface FontStyle {
45
/** Font family name */
46
fontFamily: string;
47
/** Font weight specification */
48
fontWeight: 'normal' | 'bold' | '100' | '200' | '300' | '400' | '500' | '600' | '700' | '800' | '900';
49
}
50
```
51
52
**Usage Examples:**
53
54
```typescript
55
import { NavigationContainer, DefaultTheme, DarkTheme } from '@react-navigation/native';
56
import { useColorScheme } from 'react-native';
57
58
// Automatic theme switching based on device settings
59
function App() {
60
const scheme = useColorScheme();
61
62
return (
63
<NavigationContainer theme={scheme === 'dark' ? DarkTheme : DefaultTheme}>
64
{/* Your navigation structure */}
65
</NavigationContainer>
66
);
67
}
68
69
// Always use light theme
70
function LightApp() {
71
return (
72
<NavigationContainer theme={DefaultTheme}>
73
{/* Your navigation structure */}
74
</NavigationContainer>
75
);
76
}
77
78
// Always use dark theme
79
function DarkApp() {
80
return (
81
<NavigationContainer theme={DarkTheme}>
82
{/* Your navigation structure */}
83
</NavigationContainer>
84
);
85
}
86
```
87
88
### Custom Themes
89
90
Create custom themes by extending or completely replacing the built-in themes.
91
92
```typescript { .api }
93
// Custom theme creation patterns
94
type CustomTheme = Theme;
95
96
// Theme color values for DefaultTheme
97
const DefaultThemeColors = {
98
primary: 'rgb(0, 122, 255)',
99
background: 'rgb(242, 242, 242)',
100
card: 'rgb(255, 255, 255)',
101
text: 'rgb(28, 28, 30)',
102
border: 'rgb(216, 216, 216)',
103
notification: 'rgb(255, 59, 48)',
104
};
105
106
// Theme color values for DarkTheme
107
const DarkThemeColors = {
108
primary: 'rgb(10, 132, 255)',
109
background: 'rgb(1, 1, 1)',
110
card: 'rgb(18, 18, 18)',
111
text: 'rgb(229, 229, 231)',
112
border: 'rgb(39, 39, 41)',
113
notification: 'rgb(255, 69, 58)',
114
};
115
```
116
117
**Custom Theme Examples:**
118
119
```typescript
120
// Extend existing theme
121
const CustomLightTheme = {
122
...DefaultTheme,
123
colors: {
124
...DefaultTheme.colors,
125
primary: '#007AFF',
126
notification: '#FF3B30',
127
},
128
};
129
130
// Brand-specific theme
131
const BrandTheme = {
132
...DefaultTheme,
133
colors: {
134
...DefaultTheme.colors,
135
primary: '#6366f1', // Indigo
136
background: '#f8fafc', // Slate 50
137
card: '#ffffff',
138
text: '#0f172a', // Slate 900
139
border: '#e2e8f0', // Slate 200
140
notification: '#ef4444', // Red 500
141
},
142
};
143
144
// Complete custom theme
145
const FullCustomTheme: Theme = {
146
dark: false,
147
colors: {
148
primary: '#8b5cf6', // Purple
149
background: '#faf5ff', // Purple 50
150
card: '#ffffff',
151
text: '#581c87', // Purple 900
152
border: '#c4b5fd', // Purple 300
153
notification: '#f59e0b', // Amber 500
154
},
155
fonts: {
156
regular: {
157
fontFamily: 'Inter-Regular',
158
fontWeight: '400',
159
},
160
medium: {
161
fontFamily: 'Inter-Medium',
162
fontWeight: '500',
163
},
164
bold: {
165
fontFamily: 'Inter-Bold',
166
fontWeight: '700',
167
},
168
heavy: {
169
fontFamily: 'Inter-Black',
170
fontWeight: '900',
171
},
172
},
173
};
174
175
// Dynamic theme based on user preferences
176
function AppWithDynamicTheme() {
177
const [themeMode, setThemeMode] = useState<'light' | 'dark' | 'brand'>('light');
178
179
const getTheme = () => {
180
switch (themeMode) {
181
case 'dark':
182
return DarkTheme;
183
case 'brand':
184
return BrandTheme;
185
default:
186
return DefaultTheme;
187
}
188
};
189
190
return (
191
<NavigationContainer theme={getTheme()}>
192
{/* Your navigation structure */}
193
</NavigationContainer>
194
);
195
}
196
```
197
198
### Font Configuration
199
200
Customize font families and weights for different text elements.
201
202
```typescript { .api }
203
interface FontStyle {
204
/** Font family name - should match fonts available in your app */
205
fontFamily: string;
206
/** CSS-style font weight specification */
207
fontWeight: 'normal' | 'bold' | '100' | '200' | '300' | '400' | '500' | '600' | '700' | '800' | '900';
208
}
209
210
// Default font configuration
211
const defaultFonts = {
212
regular: {
213
fontFamily: 'System',
214
fontWeight: 'normal' as const,
215
},
216
medium: {
217
fontFamily: 'System',
218
fontWeight: '500' as const,
219
},
220
bold: {
221
fontFamily: 'System',
222
fontWeight: '600' as const,
223
},
224
heavy: {
225
fontFamily: 'System',
226
fontWeight: '700' as const,
227
},
228
};
229
```
230
231
**Font Customization Examples:**
232
233
```typescript
234
// Custom font theme
235
const CustomFontTheme = {
236
...DefaultTheme,
237
fonts: {
238
regular: {
239
fontFamily: 'Roboto-Regular',
240
fontWeight: '400',
241
},
242
medium: {
243
fontFamily: 'Roboto-Medium',
244
fontWeight: '500',
245
},
246
bold: {
247
fontFamily: 'Roboto-Bold',
248
fontWeight: '700',
249
},
250
heavy: {
251
fontFamily: 'Roboto-Black',
252
fontWeight: '900',
253
},
254
},
255
};
256
257
// Mixed font families
258
const MixedFontTheme = {
259
...DefaultTheme,
260
fonts: {
261
regular: {
262
fontFamily: 'Inter-Regular',
263
fontWeight: '400',
264
},
265
medium: {
266
fontFamily: 'Inter-Medium',
267
fontWeight: '500',
268
},
269
bold: {
270
fontFamily: 'Poppins-SemiBold', // Different font for bold
271
fontWeight: '600',
272
},
273
heavy: {
274
fontFamily: 'Poppins-Bold',
275
fontWeight: '700',
276
},
277
},
278
};
279
280
// Platform-specific fonts
281
const PlatformFontTheme = {
282
...DefaultTheme,
283
fonts: {
284
regular: {
285
fontFamily: Platform.select({
286
ios: 'San Francisco',
287
android: 'Roboto',
288
default: 'System',
289
}),
290
fontWeight: '400',
291
},
292
medium: {
293
fontFamily: Platform.select({
294
ios: 'San Francisco',
295
android: 'Roboto',
296
default: 'System',
297
}),
298
fontWeight: '500',
299
},
300
bold: {
301
fontFamily: Platform.select({
302
ios: 'San Francisco',
303
android: 'Roboto',
304
default: 'System',
305
}),
306
fontWeight: '600',
307
},
308
heavy: {
309
fontFamily: Platform.select({
310
ios: 'San Francisco',
311
android: 'Roboto',
312
default: 'System',
313
}),
314
fontWeight: '700',
315
},
316
},
317
};
318
```
319
320
### Theme Usage in Components
321
322
Access theme values in your components using React Navigation's theming hooks.
323
324
```typescript { .api }
325
// Theme access is provided by @react-navigation/core
326
import { useTheme } from '@react-navigation/core';
327
328
function useTheme(): Theme;
329
```
330
331
**Component Usage Examples:**
332
333
```typescript
334
import { useTheme } from '@react-navigation/native';
335
import { View, Text, StyleSheet } from 'react-native';
336
337
// Basic theme usage
338
function ThemedComponent() {
339
const { colors, fonts } = useTheme();
340
341
return (
342
<View style={[styles.container, { backgroundColor: colors.card }]}>
343
<Text style={[fonts.bold, { color: colors.text }]}>
344
Themed Text
345
</Text>
346
</View>
347
);
348
}
349
350
// Dynamic styling based on theme
351
function AdaptiveComponent({ children }) {
352
const { colors, dark } = useTheme();
353
354
return (
355
<View style={[
356
styles.container,
357
{
358
backgroundColor: colors.card,
359
borderColor: colors.border,
360
shadowColor: dark ? colors.border : '#000',
361
shadowOpacity: dark ? 0.3 : 0.1,
362
}
363
]}>
364
{children}
365
</View>
366
);
367
}
368
369
// Theme-aware button
370
function ThemedButton({ title, onPress, variant = 'primary' }) {
371
const { colors, fonts } = useTheme();
372
373
const getButtonStyle = () => {
374
switch (variant) {
375
case 'primary':
376
return {
377
backgroundColor: colors.primary,
378
borderColor: colors.primary,
379
};
380
case 'secondary':
381
return {
382
backgroundColor: 'transparent',
383
borderColor: colors.border,
384
};
385
default:
386
return {
387
backgroundColor: colors.card,
388
borderColor: colors.border,
389
};
390
}
391
};
392
393
const getTextStyle = () => {
394
switch (variant) {
395
case 'primary':
396
return { color: '#fff' };
397
default:
398
return { color: colors.text };
399
}
400
};
401
402
return (
403
<TouchableOpacity
404
style={[styles.button, getButtonStyle()]}
405
onPress={onPress}
406
>
407
<Text style={[fonts.medium, getTextStyle()]}>
408
{title}
409
</Text>
410
</TouchableOpacity>
411
);
412
}
413
414
const styles = StyleSheet.create({
415
container: {
416
padding: 16,
417
borderWidth: 1,
418
borderRadius: 8,
419
},
420
button: {
421
paddingVertical: 12,
422
paddingHorizontal: 24,
423
borderRadius: 6,
424
borderWidth: 1,
425
alignItems: 'center',
426
},
427
});
428
```
429
430
### Theme Persistence
431
432
Save and restore user theme preferences across app sessions.
433
434
```typescript { .api }
435
// Theme persistence patterns
436
interface ThemePreference {
437
mode: 'light' | 'dark' | 'system';
438
customColors?: Partial<Theme['colors']>;
439
}
440
```
441
442
**Persistence Examples:**
443
444
```typescript
445
import AsyncStorage from '@react-native-async-storage/async-storage';
446
447
// Theme context for app-wide theme management
448
const ThemeContext = createContext<{
449
theme: Theme;
450
themeMode: 'light' | 'dark' | 'system';
451
setThemeMode: (mode: 'light' | 'dark' | 'system') => void;
452
}>({
453
theme: DefaultTheme,
454
themeMode: 'system',
455
setThemeMode: () => {},
456
});
457
458
// Theme provider with persistence
459
function ThemeProvider({ children }) {
460
const systemColorScheme = useColorScheme();
461
const [themeMode, setThemeMode] = useState<'light' | 'dark' | 'system'>('system');
462
463
// Load saved theme preference
464
useEffect(() => {
465
const loadTheme = async () => {
466
try {
467
const savedTheme = await AsyncStorage.getItem('theme-mode');
468
if (savedTheme) {
469
setThemeMode(savedTheme as 'light' | 'dark' | 'system');
470
}
471
} catch (error) {
472
console.error('Failed to load theme preference:', error);
473
}
474
};
475
476
loadTheme();
477
}, []);
478
479
// Save theme preference when changed
480
const handleThemeChange = async (mode: 'light' | 'dark' | 'system') => {
481
try {
482
await AsyncStorage.setItem('theme-mode', mode);
483
setThemeMode(mode);
484
} catch (error) {
485
console.error('Failed to save theme preference:', error);
486
}
487
};
488
489
// Determine active theme
490
const getActiveTheme = (): Theme => {
491
switch (themeMode) {
492
case 'dark':
493
return DarkTheme;
494
case 'light':
495
return DefaultTheme;
496
case 'system':
497
return systemColorScheme === 'dark' ? DarkTheme : DefaultTheme;
498
default:
499
return DefaultTheme;
500
}
501
};
502
503
const theme = getActiveTheme();
504
505
return (
506
<ThemeContext.Provider value={{
507
theme,
508
themeMode,
509
setThemeMode: handleThemeChange,
510
}}>
511
<NavigationContainer theme={theme}>
512
{children}
513
</NavigationContainer>
514
</ThemeContext.Provider>
515
);
516
}
517
518
// Theme settings screen
519
function ThemeSettingsScreen() {
520
const { themeMode, setThemeMode } = useContext(ThemeContext);
521
522
return (
523
<View>
524
<Text>Theme Settings</Text>
525
<Button
526
title="Light"
527
onPress={() => setThemeMode('light')}
528
disabled={themeMode === 'light'}
529
/>
530
<Button
531
title="Dark"
532
onPress={() => setThemeMode('dark')}
533
disabled={themeMode === 'dark'}
534
/>
535
<Button
536
title="System"
537
onPress={() => setThemeMode('system')}
538
disabled={themeMode === 'system'}
539
/>
540
</View>
541
);
542
}
543
```