コンポーネント
Web の HTML タグの代わりに、RN は専用コンポーネントを使う。
<div> や <span> は使えない。代わりに <View> や <Text>。
テキストはすべて <Text> で囲む必要があるなど、Web と少し違うルールがある。
基本: <View> と <Text>
<View>
Web の <div> 相当。すべてのレイアウトの土台。
Flexbox がデフォルトで効く(display: "flex" を書く必要なし)。
<Text>
Web の <span> + <p> 兼用。RN では文字列は必ず <Text> の中。
<View> 直下に文字列を置くとクラッシュする。
import { View, Text } from "react-native"
// BAD: View に文字列直書きは NG
<View>ハロー</View> // エラー
// GOOD
<View>
<Text>ハロー</Text>
</View>
<Text> はネスト可能で、子の <Text> は親のスタイルを継承する(RN で唯一スタイルが継承される要素)。
<Text style={{ color: "white", fontSize: 16 }}>
通常テキスト
<Text style={{ fontWeight: "bold" }}>太字部分</Text>
</Text>
<Image> と expo-image
標準の <Image>
import { Image } from "react-native"
// ローカル
<Image source={require("./assets/logo.png")} style={{ width: 80, height: 80 }} />
// リモート
<Image
source={{ uri: "https://example.com/img.jpg" }}
style={{ width: 200, height: 200 }}
/>
- 必ずサイズが必要。Web のように自動で決まらない(リモート画像は読み込むまで寸法不明のため)。
resizeMode:cover/contain/stretch/center/repeat
expo-image(推奨)
標準よりもキャッシュ・遷移・ぼかしプレビューが強い。 2026 現在は実質これがデフォルト。
import { Image } from "expo-image"
<Image
source={{ uri: "https://example.com/img.jpg" }}
style={{ width: 200, height: 200 }}
contentFit="cover"
transition={300}
placeholder={blurhash}
cachePolicy="memory-disk"
/>
スクロール: <ScrollView>
画面に収まらないコンテンツを縦/横スクロール。子の数が少ない場合用。
import { ScrollView } from "react-native"
<ScrollView contentContainerStyle={{ padding: 16, gap: 12 }}>
<Text>1</Text>
<Text>2</Text>
...
</ScrollView>
// 横スクロール
<ScrollView horizontal showsHorizontalScrollIndicator={false}> ... </ScrollView>
ScrollView はリスト用ではない
ScrollView はすべての子を一度にレンダーする。100件を超えると一気に重くなる。
リスト的なものは FlatList / SectionList / FlashList を使う。
リスト: <FlatList>
遅延レンダリングで大量データを効率良く扱う定番。
Web の react-window の概念がビルトイン。
import { FlatList, View, Text } from "react-native"
const items = [{ id: "1", name: "りんご" }, { id: "2", name: "みかん" }, ...]
<FlatList
data={items}
keyExtractor={(item) => item.id}
renderItem={({ item }) => (
<View style={{ padding: 12 }}>
<Text>{item.name}</Text>
</View>
)}
ItemSeparatorComponent={() => <View style={{ height: 1, backgroundColor: "#eee" }} />}
ListHeaderComponent={<Text>ヘッダ</Text>}
ListFooterComponent={<Text>フッタ</Text>}
ListEmptyComponent={<Text>なし</Text>}
refreshing={false}
onRefresh={() => reload()}
onEndReached={() => loadMore()}
onEndReachedThreshold={0.5}
/>
パフォーマンス最適化は パフォーマンスのページ 参照。
<SectionList>
セクションごとに分かれたリスト(連絡先のあいうえお順など)。
FlashList(Shopify 製)
FlatList より遥かに速いリストライブラリ。estimatedItemSize を渡すだけ。
Expo Router のテンプレート以降は標準で入っていることが多い。
npx expo install @shopify/flash-list
タッチ: <Pressable>
現代の RN でタッチ操作の標準。TouchableOpacity 等の旧 API は新規開発では使わない。
import { Pressable, Text } from "react-native"
<Pressable
onPress={() => console.log("tap")}
onLongPress={() => console.log("long")}
style={({ pressed }) => [
{ padding: 12, borderRadius: 8 },
{ backgroundColor: pressed ? "#0070cc" : "#0099ff" },
]}
hitSlop={8} // タップ判定の余白
android_ripple={{ color: "#fff" }} // Android のリップル
>
{({ pressed }) => (
<Text>{pressed ? "押されてる" : "押す"}</Text>
)}
</Pressable>
主要 props
onPress/onLongPress/onPressIn/onPressOutdisabled— 無効化hitSlop— タップ判定領域の拡大(小さいボタンに必須)style/childrenは関数も受け取れる(押下中の状態が引数)
<TextInput>
詳しくは フォーム。基本だけここで:
import { TextInput } from "react-native"
import { useState } from "react"
const [text, setText] = useState("")
<TextInput
value={text}
onChangeText={setText}
placeholder="入力してください"
keyboardType="default" // "email-address" / "numeric" / "phone-pad" など
autoCapitalize="none"
autoCorrect={false}
secureTextEntry={false}
multiline={false}
style={{ borderWidth: 1, padding: 8 }}
/>
その他のコアコンポーネント
<ActivityIndicator>
ローディングスピナー。
<ActivityIndicator size="large" color="#0099ff" />
<Switch>
<Switch value={on} onValueChange={setOn} />
<Modal>
画面を覆うモーダル。シンプルな全画面ダイアログに。複雑なシート系には @gorhom/bottom-sheet など別ライブラリ。
<Modal visible={open} animationType="slide" onRequestClose={() => setOpen(false)}>
<View style={{ flex: 1, padding: 24 }}>
<Text>モーダル</Text>
<Pressable onPress={() => setOpen(false)}><Text>閉じる</Text></Pressable>
</View>
</Modal>
<StatusBar>(expo-status-bar)
import { StatusBar } from "expo-status-bar"
<StatusBar style="auto" /> {/* "auto" / "light" / "dark" */}
<SafeAreaView>
ノッチや Dynamic Island、ホームインジケータなどシステム UI を避けてコンテンツを置く。
react-native-safe-area-context の SafeAreaView / useSafeAreaInsetsを使うのが定番。
import { SafeAreaView } from "react-native-safe-area-context"
<SafeAreaView style={{ flex: 1 }} edges={["top", "left", "right"]}>
<Text>ノッチを避けた範囲</Text>
</SafeAreaView>
<KeyboardAvoidingView>
キーボードが出ても入力欄が隠れないようにする。フォーム参照。
<RefreshControl>
FlatList / ScrollView の引っ張って更新。refreshing + onRefresh。
Web の div 相当のクセ
1. デフォルトの flexDirection は column
Web の Flexbox は row がデフォルトだが、RN は column。
横並びにしたい時は flexDirection: "row" を明示。
2. すべての <View> は display: flex
書かなくても Flex コンテナとして振る舞う。逆に「フローレイアウト」が無い。
3. テキストの継承は <Text> の中だけ
Web の color: inherit は基本的に効かない。子コンポーネントには明示的に props で渡す。
4. :hover がない(一部例外)
モバイルにはホバーの概念が原則ない。Web 出力時のみ onHoverIn / onHoverOut が Pressable で使える。
5. 単位はすべて DP(密度非依存ピクセル)
px や em ではなく、数値そのままが DP。14 は 14 DP。
アクセシビリティ
Web と同じくらい大事。RN は a11y の概念を踏襲した props を持つ。
accessible— その要素がフォーカス可能かaccessibilityLabel— Web のaria-labelaccessibilityHint— 補足説明accessibilityRole— "button" / "link" / "header" / "image" 等accessibilityState—{ disabled, selected, expanded, busy }
<Pressable
accessible
accessibilityRole="button"
accessibilityLabel="保存"
accessibilityState={{ disabled: !canSave }}
onPress={save}
>
<Text>保存</Text>
</Pressable>
素のコンポーネントだけで凝った UI を組むのは大変。Tamagui / NativeWind(Tailwind 風) / gluestack-ui /
react-native-paper(Material Design) / shadcn/ui の RN 移植などのデザインシステムを検討する。
スタイルだけなら スタイルのページで NativeWind を取り上げる。