Code examples
Little patterns you can lift into your app. Fix the imports to match your aliases (I like @/), eyeball package versions against your SDK, and if something touches native code, actually run it on iOS and Android.
Custom header that clears the notch
react-native-safe-area-context saves you from painting under the notch, Dynamic Island, or the home bar.
import { useSafeAreaInsets } from 'react-native-safe-area-context';
import { View, Text } from 'react-native';
export function ScreenHeader({ title }: { title: string }) {
const insets = useSafeAreaInsets();
return (
<View style={{ paddingTop: insets.top, paddingHorizontal: 16 }}>
<Text style={{ fontSize: 18, fontWeight: '600' }}>{title}</Text>
</View>
);
}FlatList that handles loading and “nothing here”
Spinner on first load, then either rows or a simple empty state—nothing fancy, just something users recognize.
import { FlatList, Text, View, ActivityIndicator } from 'react-native';
type Item = { id: string; title: string };
export function ItemList({
data,
loading,
}: {
data: Item[];
loading: boolean;
}) {
if (loading && data.length === 0) {
return (
<View style={{ flex: 1, justifyContent: 'center' }}>
<ActivityIndicator />
</View>
);
}
if (!loading && data.length === 0) {
return (
<View style={{ flex: 1, justifyContent: 'center', padding: 24 }}>
<Text style={{ textAlign: 'center' }}>Nothing here yet.</Text>
</View>
);
}
return (
<FlatList
data={data}
keyExtractor={(item) => item.id}
renderItem={({ item }) => (
<View style={{ padding: 16 }}>
<Text>{item.title}</Text>
</View>
)}
/>
);
}Search field that waits for you to stop typing
Debounce with a short timeout so you’re not hammering the API on every keystroke; cleanup on unmount included.
import { useEffect, useState } from 'react';
import { TextInput } from 'react-native';
export function SearchField({ onQuery }: { onQuery: (q: string) => void }) {
const [text, setText] = useState('');
useEffect(() => {
const id = setTimeout(() => onQuery(text.trim()), 350);
return () => clearTimeout(id);
}, [text, onQuery]);
return (
<TextInput
value={text}
onChangeText={setText}
placeholder="Search"
style={{ borderWidth: 1, borderRadius: 8, padding: 12 }}
/>
);
}Pull-to-refresh on a ScrollView
Wire RefreshControl to real async work and always clear the spinner—even when the request blows up.
import { useCallback, useState, type ReactNode } from 'react';
import { ScrollView, RefreshControl } from 'react-native';
export function RefreshableScroll({ children }: { children: ReactNode }) {
const [refreshing, setRefreshing] = useState(false);
const onRefresh = useCallback(async () => {
setRefreshing(true);
try {
await new Promise((r) => setTimeout(r, 800));
} finally {
setRefreshing(false);
}
}, []);
return (
<ScrollView
refreshControl={
<RefreshControl refreshing={refreshing} onRefresh={onRefresh} />
}
>
{children}
</ScrollView>
);
}Want the whole folder tree or the giant helper reference? That’s over in structure and app utilities.