コンポーネント

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 }}
/>

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

<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-contextSafeAreaView / 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. デフォルトの flexDirectioncolumn

Web の Flexbox は row がデフォルトだが、RN は column。 横並びにしたい時は flexDirection: "row" を明示。

2. すべての <View>display: flex

書かなくても Flex コンテナとして振る舞う。逆に「フローレイアウト」が無い。

3. テキストの継承は <Text> の中だけ

Web の color: inherit は基本的に効かない。子コンポーネントには明示的に props で渡す。

4. :hover がない(一部例外)

モバイルにはホバーの概念が原則ない。Web 出力時のみ onHoverIn / onHoverOutPressable で使える。

5. 単位はすべて DP(密度非依存ピクセル)

pxem ではなく、数値そのままが DP。14 は 14 DP。

アクセシビリティ

Web と同じくらい大事。RN は a11y の概念を踏襲した props を持つ。

<Pressable
  accessible
  accessibilityRole="button"
  accessibilityLabel="保存"
  accessibilityState={{ disabled: !canSave }}
  onPress={save}
>
  <Text>保存</Text>
</Pressable>
UI ライブラリ

素のコンポーネントだけで凝った UI を組むのは大変。Tamagui / NativeWind(Tailwind 風) / gluestack-ui / react-native-paper(Material Design) / shadcn/ui の RN 移植などのデザインシステムを検討する。
スタイルだけなら スタイルのページで NativeWind を取り上げる。