0
# Utility Functions
1
2
Helper functions for calculating dimensions, resolving titles and labels, and other common navigation tasks with platform-specific optimizations.
3
4
## Capabilities
5
6
### getDefaultHeaderHeight
7
8
Calculates platform-specific default header height considering device type, orientation, modal presentation, and platform-specific features like Dynamic Island.
9
10
```typescript { .api }
11
/**
12
* Calculate platform-specific default header height
13
* @param layout - Screen layout dimensions
14
* @param modalPresentation - Whether header is in modal presentation
15
* @param topInset - Top safe area inset (for status bar, notch, etc.)
16
* @returns Calculated header height in pixels
17
*/
18
function getDefaultHeaderHeight(
19
layout: Layout,
20
modalPresentation: boolean,
21
topInset: number
22
): number;
23
```
24
25
**Usage Examples:**
26
27
```typescript
28
import { getDefaultHeaderHeight } from "@react-navigation/elements";
29
30
// Basic header height calculation
31
function MyScreen() {
32
const [layout, setLayout] = useState({ width: 375, height: 812 });
33
const { top: topInset } = useSafeAreaInsets();
34
35
const headerHeight = getDefaultHeaderHeight(
36
layout,
37
false, // Not modal
38
topInset
39
);
40
41
return (
42
<View style={{ paddingTop: headerHeight }}>
43
<Text>Content below header</Text>
44
</View>
45
);
46
}
47
48
// Modal header height
49
function ModalScreen() {
50
const layout = useFrameSize(frame => frame);
51
const { top: topInset } = useSafeAreaInsets();
52
53
const modalHeaderHeight = getDefaultHeaderHeight(
54
layout,
55
true, // Modal presentation
56
topInset
57
);
58
59
return (
60
<View>
61
<View style={{ height: modalHeaderHeight }}>
62
<Text>Modal Header</Text>
63
</View>
64
<View style={{ flex: 1 }}>
65
<Text>Modal Content</Text>
66
</View>
67
</View>
68
);
69
}
70
71
// Dynamic header height based on orientation
72
function OrientationAwareHeader() {
73
const [layout, setLayout] = useState({ width: 375, height: 812 });
74
const { top: topInset } = useSafeAreaInsets();
75
76
useEffect(() => {
77
const subscription = Dimensions.addEventListener('change', ({ window }) => {
78
setLayout({ width: window.width, height: window.height });
79
});
80
81
return subscription?.remove;
82
}, []);
83
84
const headerHeight = getDefaultHeaderHeight(layout, false, topInset);
85
const isLandscape = layout.width > layout.height;
86
87
return (
88
<View style={{ height: headerHeight, backgroundColor: '#f8f9fa' }}>
89
<Text>
90
{isLandscape ? 'Landscape' : 'Portrait'} Header ({headerHeight}px)
91
</Text>
92
</View>
93
);
94
}
95
```
96
97
### getDefaultSidebarWidth
98
99
Calculates optimal sidebar width based on screen dimensions following Material Design guidelines and responsive design principles.
100
101
```typescript { .api }
102
/**
103
* Calculate optimal sidebar width based on screen width
104
* @param dimensions - Object containing screen width
105
* @returns Calculated sidebar width in pixels
106
*/
107
function getDefaultSidebarWidth({ width }: { width: number }): number;
108
109
/** Default drawer width constant */
110
const DEFAULT_DRAWER_WIDTH = 360;
111
112
/** Approximate app bar height constant */
113
const APPROX_APP_BAR_HEIGHT = 56;
114
```
115
116
**Usage Examples:**
117
118
```typescript
119
import { getDefaultSidebarWidth } from "@react-navigation/elements";
120
121
// Basic sidebar width calculation
122
function DrawerNavigator() {
123
const screenWidth = useFrameSize(frame => frame.width);
124
const sidebarWidth = getDefaultSidebarWidth({ width: screenWidth });
125
126
return (
127
<View style={{ flexDirection: 'row' }}>
128
<View style={{ width: sidebarWidth, backgroundColor: '#f5f5f5' }}>
129
<Text>Sidebar Content</Text>
130
</View>
131
<View style={{ flex: 1 }}>
132
<Text>Main Content</Text>
133
</View>
134
</View>
135
);
136
}
137
138
// Responsive sidebar
139
function ResponsiveSidebar() {
140
const screenWidth = useFrameSize(frame => frame.width);
141
const sidebarWidth = getDefaultSidebarWidth({ width: screenWidth });
142
const showSidebar = screenWidth > 768;
143
144
return (
145
<View style={{ flexDirection: 'row', flex: 1 }}>
146
{showSidebar && (
147
<View style={{
148
width: sidebarWidth,
149
backgroundColor: '#ffffff',
150
borderRightWidth: 1,
151
borderRightColor: '#e0e0e0'
152
}}>
153
<NavigationSidebar />
154
</View>
155
)}
156
<View style={{ flex: 1 }}>
157
<MainContent />
158
</View>
159
</View>
160
);
161
}
162
163
// Animated sidebar
164
function AnimatedSidebar({ isOpen }) {
165
const screenWidth = useFrameSize(frame => frame.width);
166
const sidebarWidth = getDefaultSidebarWidth({ width: screenWidth });
167
const animatedWidth = useRef(new Animated.Value(0)).current;
168
169
useEffect(() => {
170
Animated.timing(animatedWidth, {
171
toValue: isOpen ? sidebarWidth : 0,
172
duration: 300,
173
useNativeDriver: false
174
}).start();
175
}, [isOpen, sidebarWidth]);
176
177
return (
178
<Animated.View style={{
179
width: animatedWidth,
180
height: '100%',
181
backgroundColor: '#f8f9fa'
182
}}>
183
<SidebarContent />
184
</Animated.View>
185
);
186
}
187
```
188
189
### getHeaderTitle
190
191
Resolves header title with priority logic, supporting both string titles and function-based title renderers.
192
193
```typescript { .api }
194
/**
195
* Resolve header title with priority logic
196
* @param options - Object containing title options
197
* @param fallback - Fallback title if no options provide a title
198
* @returns Resolved title string
199
*/
200
function getHeaderTitle(
201
options: {
202
/** Simple title string */
203
title?: string;
204
/** Custom title renderer or string */
205
headerTitle?: string | ((props: HeaderTitleProps) => React.ReactNode);
206
},
207
fallback: string
208
): string;
209
```
210
211
**Usage Examples:**
212
213
```typescript
214
import { getHeaderTitle } from "@react-navigation/elements";
215
216
// Basic title resolution
217
function HeaderComponent({ route, options }) {
218
const title = getHeaderTitle(
219
{
220
title: route.params?.title,
221
headerTitle: options.headerTitle
222
},
223
route.name // fallback to route name
224
);
225
226
return (
227
<View>
228
<Text style={styles.headerTitle}>{title}</Text>
229
</View>
230
);
231
}
232
233
// Priority resolution example
234
function TitleResolver() {
235
// Priority: headerTitle > title > fallback
236
237
const title1 = getHeaderTitle(
238
{ headerTitle: "Custom Header", title: "Basic Title" },
239
"Fallback"
240
);
241
// Result: "Custom Header"
242
243
const title2 = getHeaderTitle(
244
{ title: "Basic Title" },
245
"Fallback"
246
);
247
// Result: "Basic Title"
248
249
const title3 = getHeaderTitle(
250
{},
251
"Fallback"
252
);
253
// Result: "Fallback"
254
255
return (
256
<View>
257
<Text>Title 1: {title1}</Text>
258
<Text>Title 2: {title2}</Text>
259
<Text>Title 3: {title3}</Text>
260
</View>
261
);
262
}
263
264
// Dynamic title resolution
265
function DynamicHeader({ route, navigation }) {
266
const [customTitle, setCustomTitle] = useState("");
267
268
const resolvedTitle = getHeaderTitle(
269
{
270
title: customTitle || route.params?.title,
271
headerTitle: route.params?.headerTitle
272
},
273
route.name
274
);
275
276
useLayoutEffect(() => {
277
navigation.setOptions({
278
title: resolvedTitle
279
});
280
}, [navigation, resolvedTitle]);
281
282
return (
283
<View>
284
<TextInput
285
value={customTitle}
286
onChangeText={setCustomTitle}
287
placeholder="Enter custom title"
288
/>
289
<Text>Current title: {resolvedTitle}</Text>
290
</View>
291
);
292
}
293
```
294
295
### getLabel
296
297
Resolves label text with priority-based fallback support, commonly used for button and navigation labels.
298
299
```typescript { .api }
300
/**
301
* Resolve label text with priority-based fallback
302
* @param options - Object containing label options
303
* @param fallback - Fallback label if no options provide a label
304
* @returns Resolved label string
305
*/
306
function getLabel(
307
options: {
308
/** Explicit label text (highest priority) */
309
label?: string;
310
/** Title to use as label (medium priority) */
311
title?: string;
312
},
313
fallback: string
314
): string;
315
```
316
317
**Usage Examples:**
318
319
```typescript
320
import { getLabel } from "@react-navigation/elements";
321
322
// Basic label resolution
323
function NavigationButton({ route, options }) {
324
const label = getLabel(
325
{
326
label: options.tabBarLabel,
327
title: options.title || route.params?.title
328
},
329
route.name // fallback to route name
330
);
331
332
return (
333
<TouchableOpacity>
334
<Text>{label}</Text>
335
</TouchableOpacity>
336
);
337
}
338
339
// Priority resolution examples
340
function LabelExamples() {
341
// Priority: label > title > fallback
342
343
const label1 = getLabel(
344
{ label: "Custom Label", title: "Screen Title" },
345
"Fallback"
346
);
347
// Result: "Custom Label"
348
349
const label2 = getLabel(
350
{ title: "Screen Title" },
351
"Fallback"
352
);
353
// Result: "Screen Title"
354
355
const label3 = getLabel(
356
{},
357
"Fallback"
358
);
359
// Result: "Fallback"
360
361
return (
362
<View>
363
<Text>Label 1: {label1}</Text>
364
<Text>Label 2: {label2}</Text>
365
<Text>Label 3: {label3}</Text>
366
</View>
367
);
368
}
369
370
// Tab bar implementation
371
function CustomTabBar({ state, descriptors, navigation }) {
372
return (
373
<View style={styles.tabBar}>
374
{state.routes.map((route, index) => {
375
const { options } = descriptors[route.key];
376
377
const label = getLabel(
378
{
379
label: options.tabBarLabel,
380
title: options.title
381
},
382
route.name
383
);
384
385
const isFocused = state.index === index;
386
387
return (
388
<TouchableOpacity
389
key={route.key}
390
onPress={() => navigation.navigate(route.name)}
391
style={[
392
styles.tabItem,
393
isFocused && styles.tabItemFocused
394
]}
395
>
396
<Text style={[
397
styles.tabLabel,
398
isFocused && styles.tabLabelFocused
399
]}>
400
{label}
401
</Text>
402
</TouchableOpacity>
403
);
404
})}
405
</View>
406
);
407
}
408
409
// Button with dynamic label
410
function ActionButton({ action, title, label }) {
411
const buttonLabel = getLabel(
412
{ label, title },
413
"Action" // Default fallback
414
);
415
416
return (
417
<Button onPress={action}>
418
{buttonLabel}
419
</Button>
420
);
421
}
422
```
423
424
## Platform-Specific Calculations
425
426
### iOS Header Heights
427
- **Regular headers**: 44pt base + safe area top inset
428
- **Large titles**: Up to 96pt + safe area top inset
429
- **Modal headers**: Reduced height for modal presentation
430
- **Dynamic Island**: Additional height consideration for iPhone 14 Pro models
431
432
### Android Header Heights
433
- **Material Design**: 56dp base height following Material guidelines
434
- **Status bar**: Automatic status bar height inclusion
435
- **Dense displays**: Adjusted for high-density screens
436
437
### Web Header Heights
438
- **Responsive**: Adapts to viewport size and user agent
439
- **Accessibility**: Considers user font size preferences
440
- **Browser chrome**: Accounts for browser-specific header space
441
442
## Sidebar Width Guidelines
443
444
The `getDefaultSidebarWidth` function follows these principles:
445
446
- **Mobile** (< 480px): No sidebar or overlay
447
- **Tablet** (480-768px): 280px sidebar width
448
- **Desktop** (768px+): 320-360px sidebar width
449
- **Large screens** (1200px+): Up to 400px sidebar width
450
- **Maximum**: Never exceeds 80% of screen width
451
452
## Usage Patterns
453
454
### Combining Utility Functions
455
456
```typescript
457
function AdaptiveLayout() {
458
const layout = useFrameSize(frame => frame);
459
const { top: topInset } = useSafeAreaInsets();
460
461
const headerHeight = getDefaultHeaderHeight(layout, false, topInset);
462
const sidebarWidth = getDefaultSidebarWidth(layout);
463
const showSidebar = layout.width > 768;
464
465
return (
466
<View style={{ flex: 1 }}>
467
<View style={{ height: headerHeight }}>
468
<HeaderComponent />
469
</View>
470
<View style={{ flex: 1, flexDirection: 'row' }}>
471
{showSidebar && (
472
<View style={{ width: sidebarWidth }}>
473
<SidebarComponent />
474
</View>
475
)}
476
<View style={{ flex: 1 }}>
477
<MainContent />
478
</View>
479
</View>
480
</View>
481
);
482
}
483
```
484
485
### Error Handling
486
487
All utility functions include proper error handling and default values:
488
489
```typescript
490
// Safe usage with fallbacks
491
const headerHeight = getDefaultHeaderHeight(
492
layout || { width: 375, height: 812 },
493
modalPresentation ?? false,
494
topInset ?? 0
495
);
496
497
const sidebarWidth = getDefaultSidebarWidth({
498
width: screenWidth || 375
499
});
500
501
const title = getHeaderTitle(
502
options || {},
503
"Default Title"
504
);
505
506
const label = getLabel(
507
options || {},
508
"Default Label"
509
);
510
```