第13章 実践編:TODOアプリを作る
ここから作るTODOアプリの完成形と、Expo Router・Zustand・AsyncStorageという構成にした理由を押さえます。
ここからは実践編です。これまでの章で扱ってきたコンポーネント、レイアウト、ルーティング、データ保存、ネイティブ機能の知識を組み合わせて、ひとつのアプリを最後まで作り上げます。題材は定番のTODOアプリですが、「動くサンプルが手元にある」状態を一度経験すると、新しいアプリを作るときの感覚がぐっと近くなります。
何を作るのか
完成形は次のようなアプリです。
- TODOの追加・編集・削除・完了/未完了の切り替え
- 「すべて / 未完了 / 完了」のフィルター切り替え
- 指定時刻に通知するリマインダー(ローカル通知)
- 端末を再起動してもデータが残る
- システム / ライト / ダークの3モードのテーマ切り替え
- iOS/AndroidそれぞれのネイティブなタブUI
派手な機能ではありませんが、Webフロントエンドで言えば「ToDoリストSPA」相当のものを、モバイルらしい操作感とネイティブの仕上がりで作っていきます。

左から順に、一覧画面、リマインダー付きの編集モーダル、設定画面、ダークモードの一覧画面です。iOSシミュレータ(Expo Go)で撮影しています。
本書のサンプルコードはkazutoyo/react-native-todo-app-sampleにすべて入っています。以降の章で出てくるsrc/...というパスは、このリポジトリの直下から見たものです。途中で迷ったら、対応するファイルを直接見てもらうのが一番早いです。
採用する技術スタック
第10章までで触れた技術の中から、今回は次の組み合わせを選びました。
| 関心事 | 採用 | 理由 |
|---|---|---|
| ルーティング | Expo Router (Native Tabs + Stack + Modal) | iOS/Androidネイティブのタブ表現と、モーダル遷移を素直に書ける |
| 状態管理 | Zustand | ボイラープレートが少なく、永続化ミドルウェアが揃っている |
| 永続化 | AsyncStorage | 端末ローカルで十分。サーバー同期は対象外 |
| リマインダー | expo-notifications + @react-native-community/datetimepicker | 指定時刻のローカル通知。Expo Goでも動作する |
| スタイリング | StyleSheet API + 自前のテーマ | プラットフォーム標準で依存最小、ライト/ダーク対応もしやすい |
| カタログ | Storybook for React Native | 設計したコンポーネントを単独で確認できる |
第7章で触れたとおりUniwindなどの選択肢もありますが、本書では「依存を増やさず最小構成で組む」方針を優先しています。社内ガイドラインや好みに応じて差し替えてもらって構いません。
なお状態管理にはJotaiやValtioといった選択肢もあります。Zustandを選んだ理由は、persistミドルウェアでAsyncStorageに繋ぐコードが数行で済むのと、Reactの外側でもgetState()/setState()で参照・更新できる手軽さです。
画面構成
アプリの画面はシンプルに3つです。
- TODO一覧(
/): 一覧表示、フィルター、追加ボタン - 編集(
/edit): 追加と編集を兼ねたモーダル画面 - 設定(
/settings): テーマ切り替え、Storybookへの導線(開発時のみ)
ナビゲーションのレイアウトは入れ子構造になります。
Root Stack
├── (tabs) ─ Native Tabs
│ ├── (home) Stack ─ TODO一覧
│ └── settings Stack ─ 設定
├── edit (Modal)
└── storybook
ルートのStackがネイティブタブ全体をひとつの画面として持ち、その上にモーダルとして編集画面を載せる構成です。タブの中にもStackを置くのは、将来的に各タブが詳細画面を持つことを想定して、大型タイトルなどをタブごとに調整できるようにするためです。
ディレクトリ構成
第5章で触れたsrc/配下にまとめる構成に沿って、次のように分けます。
src/
├── app/ # Expo Router の routes
│ ├── _layout.tsx # Root Stack
│ ├── (tabs)/
│ │ ├── _layout.tsx # Native Tabs
│ │ ├── (home)/
│ │ │ ├── _layout.tsx
│ │ │ └── index.tsx # TODO一覧
│ │ └── settings/
│ │ ├── _layout.tsx
│ │ └── index.tsx # 設定
│ ├── edit.tsx # 追加・編集モーダル
│ └── storybook.tsx # Storybook ルート
├── components/ # 再利用する見た目部品
│ ├── empty-state/
│ ├── fab/
│ ├── filter-bar/
│ └── todo-item/
├── hooks/
│ └── use-theme.ts
├── stores/ # Zustand のストア
│ ├── todos.ts
│ └── preferences.ts
├── theme/
│ └── colors.ts # ライト/ダークのパレット
└── types/
└── todo.ts
ポイントを2つだけ補足します。
ひとつめは、components/の各部品をディレクトリ単位で切っていることです。todo-item/todo-item.tsx、todo-item/todo-item.stories.tsx、todo-item/index.tsのように、本体・ストーリー・エクスポートをまとめて置きます。ストーリーやテストが増えても見通しが崩れません。
ふたつめは、ストアをstores/にまとめている点です。次章でくわしく扱いますが、画面側からはuseTodosStore()のようなフックだけが見えれば良く、ストア実装の詳細はディレクトリの中に閉じ込めておきます。
プロジェクトを作る
実機やシミュレータで動かす前提で、プロジェクトを作ります。Node.js 22以降をインストール済みの状態から始めてください。
npx create-expo-app sample-todo-app
cd sample-todo-app
テンプレートはデフォルトのままで構いません。生成直後はsrc/配下にサンプル画面一式が入っているので、npm run reset-projectを一度実行します。サンプルがexample/へ退避され、まっさらなsrc/appが用意されます。example/は参照用に残るだけで、以降はほとんど触らない領域です。
続いて、本書で使う主要ライブラリを入れます。npx expo installを使うと、現在のExpo SDKに合わせたバージョンが選ばれます。
npx expo install zustand @react-native-async-storage/async-storage
npx expo install react-native-safe-area-context react-native-screens
zustandはExpo SDKに紐づかないのでnpm install zustandでも問題ありませんが、本書では「依存追加はexpo installに統一する」方針なので揃えています。
react-native-safe-area-contextとreact-native-screensは、Expo Routerが内部で使うので明示的に入れておきます(テンプレートに既に含まれているはずですが、念のため)。
最後に、tsconfig.jsonにパスエイリアスを追加します。
{
"extends": "expo/tsconfig.base",
"compilerOptions": {
"strict": true,
"paths": {
"@/*": ["./src/*"]
}
},
"include": ["**/*.ts", "**/*.tsx", ".expo/types/**/*.ts", "expo-env.d.ts"]
}
@/components/...のように書けると、ファイルを別ディレクトリに動かしたときの修正がラクになります。Expo SDK 53以降のMetroはtsconfig.jsonのpathsを直接読むので、Babel側にbabel-plugin-module-resolverを入れる必要はありません。tsconfig.jsonを1箇所更新すれば型チェックとランタイムの両方に反映されます。
エージェントの準備をしておく
第1章で書いたとおり、本書はCoding Agentを使った開発を前提にしています。実装に入る前に、この先の章で効いてくるものだけ準備しておきましょう。Rozeniteやagent-deviceのような「動くアプリがあってこそ」のツールは、第20章でまとめて扱います。
Expo公式Skillsを入れる
Skillsは、エージェントに「Expoで作業するときのお作法」を教えるMarkdownのガイド集です。Expoチーム自身がexpo/skillsとして公開・メンテナンスしており、Expo Routerでの画面構成、データフェッチ、モジュール開発、EASのワークフローといった領域をカバーしています。
Claude Codeなら1行で入ります。
claude plugin install expo@claude-plugins-official
CursorやGitHub Copilotなど他のエージェントを使っている場合は、skills CLI経由で追加します。
npx skills@latest add expo/skills --skill '*'
入れたらエージェントのセッションを再起動して、SKILL.mdを読み込ませてください。これがあるだけで、生成されるコードが「今のExpoの書き方」に寄ります。逆に言えば、入れていないエージェントは数バージョン前の作法でコードを書いてくることがあります。
Expo MCPを繋ぐ
もうひとつがExpo公式のMCPサーバです。エージェントから最新のExpoドキュメントを引けるほか、EASのビルド状況やログの確認までできます。
claude mcp add --transport http expo https://mcp.expo.dev/mcp
Expoアカウントがあれば無料プランのまま使えます(月ごとの利用枠があります)。Skillsが「お作法」を教えるものだとすれば、MCPは「最新の一次情報」を引く口です。両方あると、SDKのバージョン差による食い違いがかなり減ります。
任せる範囲を決めておく
道具が揃ったところで、線引きも先に決めておきます。次章以降で作るものに当てはめると、こう考えると扱いやすいはずです。
- 任せやすい: ストアやコンポーネントの雛形作り、Storyやテストの追加、型定義の整理、繰り返しの多い画面の実装
- 自分で確かめる: 実機での見た目と手触り、iOSとAndroidの差、権限まわりの挙動、パフォーマンス
モバイルは「手元で動かしてみないと分からない」割合がWebより高い領域です。生成されたコードをそのまま信じず、実機で触る時間は確保してください。この付き合い方は第20章でもう一度整理します。
開発を進める順番
ここからの章では、次の順で組み立てていきます。
- データ層(第14章): Zustandでストアを作り、AsyncStorageに繋ぐ
- 画面とルーティング(第15章): Expo Routerで骨格を作り、3画面を実装する
- 再利用コンポーネント(第16章):
TodoItemやFilterBarなどの部品と、スワイプ・触覚フィードバックを実装する - テーマと品質(第17章): ライト/ダークテーマ、Storybook、テスト、アイコン
- 配布(第18章): EAS Buildでビルドして、ストアに公開する
順番にこだわりすぎる必要はありません。実際の開発では「画面を仮で作って、ストアを後から繋ぐ」「先にデザインの当たりをつける」など色々ですが、本書は「土台から積む」ほうが説明しやすいので、データ層から進めます。
サンプルコードを手元で動かしながら読み進めれば、各章で説明している部分が完成形のどこに対応するかがすぐに掴めます。準備ができたら次章に進んでください。