React Native Flow

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.

Project structure →App utilities →

Sponsored

Quick promo