React Native入門:ExpoとReact Navigationで最初のアプリを動かす
Web(React)経験者がReact Nativeを最短で始める手引き。Expoのセットアップ、StyleSheet、Expo Routerでの画面遷移、ネイティブ機能まで、コピペで動くコードでつまずきを潰す。
「Reactは書けるし、モバイルアプリくらい余裕でしょ」
そう思ってReact Nativeに手を出した僕の最初の画面は、真っ白でした。エラーも出ない。ただ何も映らない。原因は <div> と書いていたことです。React Nativeに div はありません。View です。<p> もない、<span> もない。CSSファイルも読み込めない。
Reactの知識はたしかに効きます。でも「Webと同じ」だと思って進むと、最初の30分でこういう小さな崖を10個くらい踏み抜きます。僕はそこで一度心が折れかけました。
この記事は、Web(React)を触ったことがある人が、その崖を踏まずにReact Nativeで最初のアプリを動かすための道順です。Expoでの始め方、見た目の作り方(StyleSheet)、画面の切り替え(Expo Router)、カメラなどのネイティブ機能まで、僕が実際につまずいた順に並べました。
この記事の要点
- React Nativeは「Reactの書き方でスマホアプリを作る」仕組み。ただし
div/p/CSSは使えず、View/Text/StyleSheetに置き換わる。 - 入門はExpo一択でいい。
npx create-expo-appで雛形が出て、スマホアプリ・iOS・Androidが同じコードで動き、最初からExpo Router(ファイルを置くだけで画面が増える仕組み)が入っている。 - 見た目は
StyleSheet.createにJSオブジェクトで書く。プロパティはbackgroundColorのようにキャメルケース。並べ方はFlexboxだが、flexDirectionの初期値がcolumn(Webのrowと逆)なのが最大の罠。 - 画面遷移は
app/フォルダにファイルを足すだけ。Linkで飛び、useRouterで戻り、useLocalSearchParamsで値を受け取る。 - カメラ等のネイティブ機能はExpo SDK(
expo-cameraなど)で完結することが多い。Claude Codeに頼むときは「権限の3状態を分けて」と最初に言うと事故らない。
React NativeはReactと何が同じで何が違う?
まず頭を切り替えます。React Nativeは「ブラウザの代わりにスマホのネイティブ部品を描くReact」です。コンポーネント、props、state、フック(useState、useEffect)はそのまま使えます。ここはWebの貯金がそっくり効きます。
崖になるのは「画面に出すもの」と「見た目の付け方」です。Webの感覚を持ち込むと壊れる部分を表にしました。
| Web (React) | React Native | ひとことメモ |
|---|---|---|
<div> | <View> | 箱。レイアウトの基本単位 |
<p> / <span> | <Text> | 文字は必ず Text で囲む。地のテキストは置けない |
<button> | <Pressable> | タップできる領域。onPress で反応する |
<img> | <Image> | source={{ uri: '...' }} で読む |
<input> | <TextInput> | value と onChangeText で制御 |
.css / className | StyleSheet.create | クラス名ではなくJSオブジェクトを style に渡す |
onClick | onPress | クリックではなくタップ |
「文字は必ず Text で囲む」は最初の鬼門です。Webなら <div>こんにちは</div> で済みますが、React Nativeで <View>こんにちは</View> と書くと落ちます。<View><Text>こんにちは</Text></View> が正解です。
公式の考え方は React Native公式のComponents and APIs に一覧があります。最初は View / Text / Image / TextInput / Pressable / ScrollView / FlatList の7つだけ覚えれば、ほとんどの画面は組めます。
Claude Codeをまだ入れていないなら、先にClaude Code入門:インストールから最初の30分で成果を出す始め方を済ませておくと、この後のコード生成が一気に楽になります。Web側のReactそのものの作法はClaude CodeでReact開発を実戦投入する方法に分けてあるので、JSXやフックが不安な人はそちらから。
Expoで雛形を出す(入門はこれ一択)
React Nativeを生で始めると、Android StudioとXcodeのインストールで日が暮れます。僕は1日溶かしました。入門ならExpoを使ってください。面倒なネイティブ環境を肩代わりしてくれます。
Node.jsが入っていれば、次のコマンドで雛形が出ます。
# 雛形を作る(最初からExpo Router入りのテンプレート)
npx create-expo-app@latest rn-first-app
cd rn-first-app
# 開発サーバーを起動
npx expo start
起動するとQRコードが出ます。スマホに Expo Go アプリ(App Store / Google Playで無料)を入れて、そのQRを読むと、自分のスマホに今書いたアプリが映ります。ビルドもケーブル接続もいりません。これが感動ポイントです。
PCのシミュレーターで見たい場合は、npx expo start を動かしたターミナルでキーを押すだけです。
a… Android Emulator(Android Studioが必要)i… iOS Simulator(macOS + Xcodeが必要)w… Webブラウザ
最初はスマホ + Expo Goがいちばん手軽です。シミュレーターは後から足せます。インストール周りで詰まったら Expo公式のGet started が最新の手順です。
ちなみに create-expo-app の雛形は app/ フォルダ・ファイルベースのルーティング(後述)が最初から入っています。昔のReact Native入門記事だと「React Navigationを手で設定する」話が出てきますが、いまのExpoは最初から画面遷移の仕組みが乗っている、と覚えておいてください。
見た目はStyleSheetで作る(CSSとの違い)
React NativeにCSSファイルはありません。見た目はJavaScriptのオブジェクトで書き、StyleSheet.create でまとめます。書き味はCSSに似ていますが、プロパティ名が background-color ではなく backgroundColor のキャメルケースになります。
ここで僕がいちばんハマったのがFlexboxの初期値です。Webのdivは縦に積まれますが、display: flex を当てると横並びになります(flex-direction: row が既定)。React Nativeは逆で、**すべてのViewが最初からFlexboxで、flexDirection の既定値が column(縦積み)**です。だから「横に並べたいのに縦に並ぶ!」で必ず一度悩みます。横に並べたいときは明示的に flexDirection: 'row' を書きます。
下は、ボタンを押すとカウントが増える、コピペで動く1画面です。app/index.tsx をこの中身に置き換えれば、そのまま npx expo start で動きます。
// app/index.tsx
// カウンター画面。Expoの雛形の app/index.tsx をこの内容に差し替えて使う。
import { useState } from 'react';
import { Pressable, StyleSheet, Text, View } from 'react-native';
export default function HomeScreen() {
const [count, setCount] = useState(0);
return (
<View style={styles.container}>
<Text style={styles.title}>タップした回数</Text>
{/* 数字。Webの <p> ではなく必ず <Text> で囲む */}
<Text style={styles.count}>{count}</Text>
{/* 横並びにしたいので flexDirection: 'row' を指定 */}
<View style={styles.row}>
<Pressable
accessibilityRole="button"
onPress={() => setCount((c) => Math.max(0, c - 1))}
style={({ pressed }) => [styles.button, pressed && styles.pressed]}
>
<Text style={styles.buttonText}>−1</Text>
</Pressable>
<Pressable
accessibilityRole="button"
onPress={() => setCount((c) => c + 1)}
style={({ pressed }) => [styles.button, pressed && styles.pressed]}
>
<Text style={styles.buttonText}>+1</Text>
</Pressable>
</View>
</View>
);
}
const styles = StyleSheet.create({
// 画面全体。中央寄せ。flexDirection は書かないので既定の column(縦積み)
container: { flex: 1, alignItems: 'center', justifyContent: 'center', gap: 16, backgroundColor: '#0f172a' },
title: { fontSize: 16, color: '#94a3b8' },
count: { fontSize: 64, fontWeight: '800', color: '#f8fafc' },
row: { flexDirection: 'row', gap: 12 }, // ここで横並びにする
button: { paddingHorizontal: 24, paddingVertical: 14, borderRadius: 12, backgroundColor: '#2563eb' },
pressed: { opacity: 0.7 }, // 押している間だけ薄くする
buttonText: { fontSize: 20, fontWeight: '700', color: '#ffffff' },
});
ポイントは3つ。style に渡すのは配列でもよく、[styles.button, pressed && styles.pressed] のように後ろが優先で上書きできること。gap が使えるので余白の指定が楽なこと。そして数字を <Text> で囲んでいること。StyleSheetの正式な仕様は React Native公式のStyle を見てください。
画面を増やす:Expo Routerのファイルベース遷移
アプリは普通、複数画面あります。React Nativeの画面遷移ライブラリの定番が React Navigation で、それをファイルベースで使いやすくしたのが Expo の Expo Router です。いまの create-expo-app 雛形にはExpo Routerが入っているので、これを前提に進めます。
仕組みは驚くほど単純で、app/ フォルダにファイルを置くと、それがそのまま画面(ルート)になる。Next.jsのApp Routerを触ったことがあるなら、ほぼ同じ感覚です。
app/
_layout.tsx ← 画面の入れ物(共通の枠・遷移方法を決める)
index.tsx ← "/"(最初の画面)
details.tsx ← "/details"(2枚目の画面)
_layout.tsx で「スタック型の遷移(カードが上に積まれて戻れる)」を宣言します。
// app/_layout.tsx
// 画面をスタック(積み重ね)で管理する。これで「進む・戻る」が効くようになる。
import { Stack } from 'expo-router';
export default function RootLayout() {
return (
<Stack>
<Stack.Screen name="index" options={{ title: 'ホーム' }} />
<Stack.Screen name="details" options={{ title: '詳細' }} />
</Stack>
);
}
最初の画面から2枚目へは Link で飛びます。URLのクエリのように値も渡せます。
// app/index.tsx(遷移を試す版)
import { Link } from 'expo-router';
import { StyleSheet, Text, View } from 'react-native';
export default function HomeScreen() {
return (
<View style={styles.container}>
<Text style={styles.title}>ホーム画面</Text>
{/* params で値を渡せる。受け取り側で useLocalSearchParams を使う */}
<Link href={{ pathname: '/details', params: { id: '42' } }} style={styles.link}>
詳細を見る →
</Link>
</View>
);
}
const styles = StyleSheet.create({
container: { flex: 1, alignItems: 'center', justifyContent: 'center', gap: 16 },
title: { fontSize: 22, fontWeight: '700' },
link: { fontSize: 18, color: '#2563eb' },
});
受け取る側はこうです。useLocalSearchParams で値を読み、useRouter で前の画面に戻ります。
// app/details.tsx
import { useLocalSearchParams, useRouter } from 'expo-router';
import { Pressable, StyleSheet, Text, View } from 'react-native';
export default function DetailsScreen() {
const { id } = useLocalSearchParams<{ id: string }>(); // 受け取った値
const router = useRouter();
return (
<View style={styles.container}>
<Text style={styles.title}>詳細画面</Text>
<Text style={styles.body}>受け取ったID: {id}</Text>
<Pressable accessibilityRole="button" onPress={() => router.back()} style={styles.button}>
<Text style={styles.buttonText}>← 戻る</Text>
</Pressable>
</View>
);
}
const styles = StyleSheet.create({
container: { flex: 1, alignItems: 'center', justifyContent: 'center', gap: 16 },
title: { fontSize: 22, fontWeight: '700' },
body: { fontSize: 16, color: '#374151' },
button: { paddingHorizontal: 20, paddingVertical: 12, borderRadius: 10, backgroundColor: '#111827' },
buttonText: { color: '#fff', fontWeight: '700' },
});
これだけで「ホーム → 詳細 → 戻る」が動きます。下タブ(Instagramの下にある切り替えバー)が欲しければ、app/(tabs)/_layout.tsx を作って Stack の代わりに Tabs を使います。詳しい考え方は Expo Router公式のIntroduction にまとまっています。
「画面を1枚足して」とClaude Codeに頼むときは、**「Expo Routerのファイルベースで」「app/ 配下に」**と明示してください。これを言わないと、古い記事を学習した影響でReact Navigationを手書きで設定するコードを出してくることがあります。
ネイティブ機能(カメラ等)はExpo SDKで足す
スマホアプリの醍醐味はカメラ・位置情報・通知といったネイティブ機能です。生のReact Nativeだとネイティブコードを書く話になりますが、Expoなら Expo SDK のパッケージを入れるだけで多くが片付きます。
例としてカメラを足します。インストールはこうです。
# カメラ機能を追加
npx expo install expo-camera
カメラで詰まる定番が権限です。「まだ聞いていない(未判定)」「拒否された」「許可済み」の3状態があり、ここを雑に書くと真っ黒画面やクラッシュになります。だからClaude Codeに頼むときは「権限の3状態を分けて、未許可のときは許可ボタンを出して」と最初に指定するのがコツです。下はコピペで動く最小のカメラ画面です。
// app/camera.tsx
// 権限の3状態(未判定・拒否・許可)を分けて扱うカメラ画面。expo-camera が必要。
import { CameraView, useCameraPermissions } from 'expo-camera';
import { Pressable, StyleSheet, Text, View } from 'react-native';
export default function CameraScreen() {
const [permission, requestPermission] = useCameraPermissions();
// 状態1: まだ権限を聞いていない(読み込み中)
if (!permission) {
return (
<View style={styles.center}>
<Text>カメラの権限を確認中…</Text>
</View>
);
}
// 状態2: 許可されていない → 許可ボタンを出す
if (!permission.granted) {
return (
<View style={styles.center}>
<Text style={styles.title}>カメラの利用許可が必要です</Text>
<Pressable
accessibilityRole="button"
accessibilityLabel="カメラの利用を許可する"
onPress={requestPermission}
style={styles.button}
>
<Text style={styles.buttonText}>許可する</Text>
</Pressable>
</View>
);
}
// 状態3: 許可済み → カメラを表示
return (
<View style={styles.container}>
<CameraView style={styles.camera} facing="back" />
</View>
);
}
const styles = StyleSheet.create({
container: { flex: 1, backgroundColor: '#000' },
center: { flex: 1, alignItems: 'center', justifyContent: 'center', gap: 16, padding: 24 },
camera: { flex: 1 },
title: { fontSize: 18, fontWeight: '700' },
button: { paddingHorizontal: 20, paddingVertical: 12, borderRadius: 10, backgroundColor: '#2563eb' },
buttonText: { color: '#fff', fontWeight: '700' },
});
ここで一つ大事な前提。Expo Goに入っていない種類のネイティブ機能を足すと、Expo Goでは動かないことがあります。その場合は「development build(自分用にネイティブ部分を含めて作り直したExpoアプリ)」が必要になります。カメラ程度なら最近のExpo Goでも動きますが、独自SDKや決済端末を足すなら development build に切り替える、と覚えておいてください。詳細は Expo公式のExpo Go vs development builds を確認します。
権限の扱いは見落とすとアプリ審査でも弾かれます。読み上げ対応も含めた観点はClaude Codeでアクセシビリティ対応を実装する実践ワークフローに整理してあります。
Web(React)から移行するときに踏む崖トップ5
最後に、僕がWebからReact Nativeに来て実際に踏んだ崖を、踏む順に並べます。先に知っていれば回避できます。
div/p/spanを書いてしまう。 →View/Textに置き換える。地のテキストは必ずTextで囲む。- CSSファイルを作ろうとする。 → ファイルは作れない。
StyleSheet.createにJSオブジェクトで書く。px単位も書かない(数値だけ)。 flexDirectionの既定がcolumn。 → 横並びはflexDirection: 'row'を明示。Webと逆なのでここで必ず1回詰まる。onClickを書く。 →onPress。クリックではなくタップ。要素も<button>ではなく<Pressable>。- 画面遷移をWebルーターで考える。 →
react-routerではなくExpo Router。app/にファイルを置けば画面が増える。hrefも/detailsのようなパス。
加えて、window や document、localStorage はありません。永続化は expo-secure-store や @react-native-async-storage/async-storage を使います。alert() は Alert.alert() です。この「Web APIが無い」系は、Claude Codeに「これはReact Native。Web APIは使わずExpo/RNのAPIで」と一言添えると、的外れなコードがぐっと減ります。
UIのテストを並行して書きたい人はClaude CodeでTDDを実践する方法、別フレームワークとの比較が気になる人はFlutter入門の壁はウィジェットツリー。Claude CodeでDart状態管理まで突破するも合わせてどうぞ。
よくある質問
Q. React NativeとExpoの違いは? どっちを使えばいい? React Nativeが土台のフレームワーク、Expoはそれを「環境構築なしですぐ動かせる」ようにした道具立てです。入門は迷わずExpoでいいです。あとから生のネイティブが必要になっても、Expoのままネイティブコードを足せます(bare workflow)。最初からXcode/Android Studioを触る必要はありません。
Q. Expo Goだけで本番アプリを出せますか? Expo Goは「お試し実行アプリ」で、リリース用ではありません。本番はEAS Build等で自分のアプリとしてビルドします。また、Expo Goに含まれないネイティブ機能を足すと development build が必要になります。Expo Goで動いた=本番でも同じ、とは限らない点に注意です。
Q. CSSの知識はどこまで使えますか?
Flexboxの考え方(flex、alignItems、justifyContent)はほぼそのまま使えます。違いは、プロパティがキャメルケースなこと、単位(px等)を書かず数値で指定すること、flexDirection の既定が column なこと。グリッドや擬似要素、メディアクエリはありません。
Q. React Navigationは別で入れなくていいの?
いまの create-expo-app 雛形はExpo Router(内部でReact Navigationを使う)が最初から入っているので、自分で @react-navigation/* を設定する必要はありません。app/ にファイルを足すだけで画面が増えます。手動設定の解説記事は古い前提のことが多いので注意してください。
Q. Claude Codeにモバイル開発を任せるときのコツは?
「これはExpo + Expo Routerのプロジェクト」「Web APIは使わない」「権限は3状態を分ける」「変更したファイル・実行コマンド・未検証の端末を最後に報告して」と、前提と完了条件を先に渡すことです。広い副作用を持つコマンド(expo prebuild など)は確認を挟む設定にしておくと安全で、やり方はClaude Code 権限設定リファレンスにまとめてあります。
実際に試した結果
Webから来た僕がReact Nativeで最初の壁を越えられたのは、結局「Webの知識のどこが効いて、どこが効かないか」を最初に線引きしたからでした。コンポーネント・props・state・フックはそのまま、divとCSSとルーターだけ頭を入れ替える。これだけで真っ白画面は止まりました。
Claude Codeに頼むときも同じで、「これはExpoプロジェクトで、Web APIは使わない」と一行足すだけで、document を触るような的外れなコードが出なくなりました。flexDirection: 'column' だけは、いまだに横並びを作るたびに一瞬手が止まりますが。
入門の順番としては、(1) create-expo-app で雛形を出してExpo Goで動かす、(2) StyleSheet で1画面作る、(3) app/ にファイルを足して遷移させる、(4) Expo SDKでネイティブ機能を1つ足す——この4ステップが、僕には一番つまずきが少なかったです。
実装プロンプトや設定テンプレートをまとめて使いたい人は教材一覧、チームでExpo/権限/リリースまで固めたい人は研修・導入相談もどうぞ。まずは上のカウンターを自分のスマホで動かすところから始めてください。動いた瞬間、たぶん少しニヤッとします。
無料PDF: Claude Code はじめてのチートシート
まずは無料PDFで基本コマンドと最初の使い方をまとめて確認してください。登録後はそのままテンプレート集や導入相談にも進めます。
スパムは送りません。登録情報は厳重に管理します。
Claude Codeを仕事で使える形にしませんか?
まず無料PDFで基本を固め、繰り返し使う作業はGumroad教材へ、チーム導入や権限設計は導入相談へ進めます。
この記事を書いた人
Masa
Claude Codeの実務活用、導入設計、収益導線改善を検証しているエンジニア。10言語の技術メディアを運営中。
関連書籍・参考図書
この記事のテーマに関連する書籍を楽天ブックスで探せます。
※ 当サイトは楽天市場のアフィリエイトプログラムに参加しています。上記リンクから商品をご購入いただくと、運営者に紹介料が支払われる場合があります。
関連記事
制作会社がClaude Codeに触らせる前に決める権限チェックリスト
クライアントサイトを壊さずにAI編集を使うための、制作会社向け権限と確認の型です。
SaaSサポートのバグ報告をClaude Codeで再現手順に変える実務フロー
問い合わせ文をそのまま開発へ投げず、再現手順、証拠、次の一手に整えるサポート向け手順です。
Obsidianの古いメモをClaude Codeの指示書に変える10分ルーチン
Obsidianに溜めたメモが毎回ゴミになる人へ。事実・決定・未確認に仕分けして、Claude Codeがそのまま動ける指示書に変える朝の10分の型を紹介します。