Released · improving
React Native guide · 3/6
You describe the UI with React components, and React Native renders them as real native views, not as a web page. This chapter covers core components, styling, platform differences, navigation and native modules.
Instead of HTML elements such as div and p, you use components provided by React Native.
| Component | Purpose | Android / iOS equivalent |
|---|---|---|
View | layout container | ViewGroup / UIView |
Text | displays text | TextView / UITextView |
Image | displays images | ImageView / UIImageView |
TextInput | text entry | EditText / UITextField |
ScrollView | scrollable area | ScrollView / UIScrollView |
FlatList | long lists, renders only what is visible | virtualized list |
Pressable | handles presses | touch events |
All text must be inside a Text component; putting a raw string directly in a View is an error.
import { Image, Pressable, Text, View } from "react-native";
export function Profile({ onFollow }: { onFollow: () => void }) {
return (
<View style={{ padding: 16, gap: 8 }}>
<Image source={{ uri: "https://reactnative.dev/img/tiny_logo.png" }} style={{ width: 64, height: 64 }} />
<Text style={{ fontSize: 18, fontWeight: "600" }}>React Native</Text>
<Pressable onPress={onFollow}>
<Text>Follow</Text>
</Pressable>
</View>
);
}Network images need an explicit width and height. Local images use require("./assets/icon.png").
ScrollView renders every child at once, which gets slow with many items. For long lists, use FlatList, which renders only what is on screen.
import { FlatList, Text } from "react-native";
type Todo = { id: string; title: string };
export function TodoList({ items }: { items: Todo[] }) {
return (
<FlatList
data={items}
keyExtractor={(item) => item.id}
renderItem={({ item }) => <Text style={{ padding: 12 }}>{item.title}</Text>}
ListEmptyComponent={<Text>Nothing to do.</Text>}
/>
);
}Styles are JavaScript objects whose keys mirror CSS properties in camelCase. Unitless numbers are density-independent pixels, and styles do not cascade from parent to child (only nested Text elements inherit some text styles). Layout uses Flexbox, but unlike the web the default flexDirection is column.
import { StyleSheet, Text, View } from "react-native";
export function Row() {
return (
<View style={styles.row}>
<Text style={styles.label}>Name</Text>
<Text style={[styles.label, styles.value]}>Ada Lovelace</Text>
</View>
);
}
const styles = StyleSheet.create({
row: { flexDirection: "row", justifyContent: "space-between", padding: 16 },
label: { fontSize: 16 },
value: { fontWeight: "bold" },
});An array passed to style is merged, later entries winning, which suits conditional styles.
When a platform needs different handling, use the Platform API or platform-specific file extensions.
import { Platform, StyleSheet } from "react-native";
const styles = StyleSheet.create({
card: {
padding: 16,
...Platform.select({
ios: { shadowColor: "#000", shadowOpacity: 0.1, shadowRadius: 8 },
android: { elevation: 3 },
}),
},
});
export const isIOS = Platform.OS === "ios";If you create Button.ios.tsx and Button.android.tsx and import ./Button, the bundler picks the right file for each platform. To stay clear of notches and status bars, use SafeAreaView from react-native-safe-area-context; for screen size, use the useWindowDimensions hook.
React Native itself has no navigation. Expo projects use Expo Router, while CLI projects usually use React Navigation; Expo Router is built on top of React Navigation.
import { Link, router, useLocalSearchParams } from "expo-router";
import { Button, Text, View } from "react-native";
// app/posts/[id].tsx
export default function Post() {
const { id } = useLocalSearchParams<{ id: string }>();
return (
<View>
<Text>Post {id}</Text>
<Link href="/">Home</Link>
<Button title="Next post" onPress={() => router.push(`/posts/${Number(id) + 1}`)} />
</View>
);
}Device features beyond JavaScript are exposed through native modules written in Kotlin, Java, Swift or Objective-C. Most already exist as libraries; check the React Native Directory.
React Native now runs on the New Architecture by default. The old design passed asynchronous, serialized messages over a bridge; the New Architecture uses JSI so JavaScript can hold references to native objects and call them directly, synchronously when needed. Native modules become lazily loaded Turbo Native Modules, the renderer is Fabric, and Codegen generates type-safe glue code from TypeScript specs. The default JavaScript engine is Hermes.
import type { TurboModule } from "react-native";
import { TurboModuleRegistry } from "react-native";
export interface Spec extends TurboModule {
setItem(value: string, key: string): void;
getItem(key: string): string | null;
}
export default TurboModuleRegistry.getEnforcing<Spec>("NativeLocalStorage");Expo projects can also use the Expo Modules API, a simpler way to write modules in Swift and Kotlin (npx create-expo-module@latest --local).
View, Text, Image and FlatList; they render as native views.StyleSheet and Flexbox, where the default direction is column.Platform and .ios / .android files.
0 comments
Sign in · Sign in to leave a comment.
Be the first to comment.