contentintech

React Native Cheatsheet

Quick reference for React Native and Expo — CLI commands, core components, Flexbox styling, hooks, Expo Router navigation, FlatList performance, native device APIs, and common patterns.

ExpoComponentsNavigationHooks
NotesCheatsheet

CLI & Expo Commands

Project & Dev Server

npx create-expo-app@latest my-app   # scaffold (Expo Router + TS)
npx expo start                       # start dev server
npx expo start --clear               # clear Metro cache
# in terminal: i = iOS, a = Android, r = reload

npx expo install <pkg>                # install a compatible native lib
npx expo prebuild                    # generate ios/ + android/ (CNG)
npx expo run:ios                     # build + run local iOS
npx expo run:android                 # build + run local Android
npx expo-doctor                      # diagnose project issues

EAS (Build / Submit / Update)

eas login
eas build:configure
eas build --platform ios --profile production
eas build -p android --profile preview
eas submit -p ios                    # upload to App Store Connect
eas update --branch production -m "hotfix"   # OTA JS update

Core Components

Primitives

ComponentUse
<View>Layout box (Flexbox container)
<Text>Text — all strings must be inside this
<Image>Image; prefer expo-image for caching
<ScrollView>Small scrollable content, all rendered
<FlatList>Virtualized list for arrays
<Pressable>Touch target (onPress, onLongPress)
<TextInput>Editable text field
<SafeAreaView>Avoid notch / status bar overlap

Minimal Screen

import { View, Text, Pressable } from "react-native";

export default function Screen() {
  return (
    <View style={{ flex: 1, padding: 16 }}>
      <Text>Hello</Text>
      <Pressable onPress={() => {}}>
        <Text>Tap</Text>
      </Pressable>
    </View>
  );
}

Styling & Flexbox

StyleSheet

import { StyleSheet } from "react-native";

const s = StyleSheet.create({
  box: {
    flex: 1,
    flexDirection: "row",      // default is "column"
    justifyContent: "center",  // main axis
    alignItems: "center",      // cross axis
    gap: 8,
    padding: 16,
    backgroundColor: "#0f172a",
    borderRadius: 12,
  },
});
// combine / conditional: style={[s.box, active && s.active]}

Flexbox Keys

PropertyValues
flexDirectioncolumn (default), row
justifyContentcenter, space-between, flex-end
alignItemscenter, stretch, flex-start
flex1 = fill available space
gapspacing between children

Hooks

State & Effects

const [n, setN] = useState<number>(0);
setN((c) => c + 1);                 // functional update

useEffect(() => {
  const id = setInterval(tick, 1000);
  return () => clearInterval(id);   // cleanup
}, []);                             // [] = run once on mount

const onPress = useCallback(() => setN((c) => c + 1), []);
const total = useMemo(() => heavy(items), [items]);
const ref = useRef<TextInput>(null);   // ref.current.focus()

Expo Router Navigation

File → Route

app/index.tsx        -> /
app/about.tsx        -> /about
app/posts/[id].tsx   -> /posts/:id   (dynamic)
app/(tabs)/home.tsx  -> /home        (group, no URL segment)
app/_layout.tsx      -> Stack / Tabs navigator

Navigate & Params

import { Link, router, useLocalSearchParams } from "expo-router";

<Link href="/posts/42">Open</Link>   // declarative
router.push("/posts/42");            // push new screen
router.replace("/login");            // no back
router.back();                       // go back

const { id } = useLocalSearchParams<{ id: string }>();

Stack & Tabs Layout

import { Stack, Tabs } from "expo-router";

// app/_layout.tsx
<Stack>
  <Stack.Screen name="index" options={{ title: "Home" }} />
</Stack>

// app/(tabs)/_layout.tsx
<Tabs screenOptions={{ tabBarActiveTintColor: "#14b8a6" }}>
  <Tabs.Screen name="home" options={{ title: "Home" }} />
</Tabs>

Lists & Performance

Optimized FlatList

<FlatList
  data={items}
  keyExtractor={(it) => it.id}       // stable key, not index
  renderItem={({ item }) => <Row item={item} />}
  initialNumToRender={10}
  windowSize={5}
  removeClippedSubviews
  ItemSeparatorComponent={Sep}
  ListEmptyComponent={<Empty />}
  refreshing={loading}
  onRefresh={refetch}                // pull to refresh
  onEndReached={loadMore}            // infinite scroll
  onEndReachedThreshold={0.5}
/>

const Row = memo(({ item }) => <Text>{item.title}</Text>);

Perf Tips

DoWhy
React.memo rowsSkip re-render of unchanged items
useCallback handlersStable props for memoized children
FlashList (Shopify)Faster for huge / mixed lists
ReanimatedAnimations on the UI thread

Native APIs & Storage

Permissions & Devices

import * as Location from "expo-location";
import { useCameraPermissions, CameraView } from "expo-camera";

await Location.requestForegroundPermissionsAsync();
await Location.getCurrentPositionAsync({});

const [perm, request] = useCameraPermissions();
if (!perm?.granted) await request();

Storage

import AsyncStorage from "@react-native-async-storage/async-storage";
import * as SecureStore from "expo-secure-store";

await AsyncStorage.setItem("k", JSON.stringify(v));
const v = JSON.parse((await AsyncStorage.getItem("k")) ?? "null");

await SecureStore.setItemAsync("token", jwt);   // sensitive data
const jwt = await SecureStore.getItemAsync("token");

Common Patterns

Quick Reference

TaskApproach
Fetch + cache data@tanstack/react-query
Global statezustand / Context
Formsreact-hook-form + zod
Loading spinner<ActivityIndicator />
Platform checkPlatform.OS === "ios"
Screen sizeuseWindowDimensions()
Safe area insetsuseSafeAreaInsets()
Tailwind stylingnativewind
Icons@expo/vector-icons

Platform-Specific Code

import { Platform } from "react-native";

const pad = Platform.OS === "ios" ? 20 : 16;
Platform.select({ ios: "SF Pro", android: "Roboto" });
// or use platform file extensions: Button.ios.tsx / Button.android.tsx

Section navigation