Skip to main content

React Native推奨構成

2026年時点でのReact Nativeアプリ開発における推奨技術スタックと構成をまとめる。

この構成は、Reactエンジニアが既存のWeb開発知識を活かしながら、モバイルアプリを効率的に開発できることを重視している。

推奨技術スタック​

新規プロジェクトで採用すべき技術スタックは以下の通りである。

レイヤー推奨立ち位置
言語TypeScript標準
フレームワークExpoNext.js + Vercelの役割を果たす
サーバー状態管理TanStack QueryWebと同じものがそのまま動く
クライアント状態管理ZustandWebと同じものがそのまま動く
API通信fetch(+ Hono RPC)Webと同じ感覚で書ける
フォームReact Hook Form + ZodWebと同じパターン
スタイリングNativeWindRN版Tailwind CSS
UIコンポーネントgluestack-uiRN版shadcn/ui
ローカルストレージMMKVlocalStorageの超高速版
バックエンド/認証SupabaseFirebase的BaaS

なぜこの構成なのか​

この技術スタックの最大の特徴は、Web開発の知識をそのまま再利用できる点にある。

  • TanStack Query、Zustand、React Hook Form、Zodは、Webで使っているものと完全に同じ
  • Expo RouterはNext.jsのApp Routerに似たファイルベースルーティング
  • NativeWindはTailwind CSSの書き味を再現
  • gluestack-uiはshadcn/uiと同じコピペ方式
  • Hono / SupabaseはWebのバックエンド構成と共通

アーキテクチャの理解​

New Architecture​

React Native 0.76から、New Architectureがデフォルトになった。これにより以下が実現された。

  • JSI(JavaScript Interface): JavaScriptとネイティブ間で直接参照を持てる仕組み
  • Fabric: 新しいUI描画エンジン(React 18の並行レンダリングに対応)
  • TurboModules: 遅延読み込み方式のNative Module
  • Codegen: 型安全なバインディングコードの自動生成

新規プロジェクトでは特別な設定は不要で、最新のExpo SDKを使えば自動的にNew Architecture上で動作する。

主要ライブラリの使い方​

Expo​

React Nativeアプリ開発の標準レールとなっている。

開発時の利点​

  • Expo Go: QRコードで実機確認(Xcode不要)
  • Development Build: アプリ固有の設定を反映した開発ビルド
  • CNG(Continuous Native Generation): 設定ファイル(app.json)からネイティブプロジェクトを自動生成

配信時の利点(EAS)​

  • EAS Build: クラウドでビルド実行(Macなしでも可能)
  • EAS Submit: ストア提出をコマンド1つで完了
  • EAS Update: JavaScriptの更新をOTA配信(審査不要)

Expo Router​

ファイルベースルーティングを実現する。

app/
├── _layout.tsx # ルートレイアウト
├── (tabs)/ # タブグループ
│ ├── _layout.tsx # タブナビゲーション定義
│ ├── index.tsx # ホームタブ
│ └── settings.tsx # 設定タブ
└── users/
└── [id].tsx # 動的ルート

状態管理の役割分担​

状態は大きく2種類に分ける。

  • Server State: サーバーに本体があるデータ → TanStack Query
  • Client State: アプリ内に本体がある状態 → Zustand

TanStack Queryの例​

import { useQuery } from '@tanstack/react-query';
import { z } from 'zod';

const profileSchema = z.object({
id: z.string(),
name: z.string(),
avatarUrl: z.url().nullable(),
});

async function fetchProfile(userId: string) {
const res = await fetch(`https://api.example.com/users/${userId}`);
if (!res.ok) throw new Error('プロフィールの取得に失敗しました');
return profileSchema.parse(await res.json());
}

export function useProfile(userId: string) {
return useQuery({
queryKey: ['profile', userId],
queryFn: () => fetchProfile(userId),
});
}

Zustandの例​

import { create } from 'zustand';

type Theme = 'light' | 'dark' | 'system';

type ThemeState = {
theme: Theme;
setTheme: (theme: Theme) => void;
};

export const useThemeStore = create<ThemeState>((set) => ({
theme: 'system',
setTheme: (theme) => set({ theme }),
}));

API通信とHono RPC​

バックエンドも自分で書く場合、Honoを使うと型安全なRPCが実現できる。

サーバー側​

import { Hono } from 'hono';
import { zValidator } from '@hono/zod-validator';
import { z } from 'zod';

const app = new Hono()
.get('/todos', (c) => {
return c.json({ todos: [{ id: 1, title: '牛乳を買う', done: false }] });
})
.post(
'/todos',
zValidator('json', z.object({ title: z.string().min(1) })),
(c) => {
const { title } = c.req.valid('json');
return c.json({ ok: true, title }, 201);
}
);

export type AppType = typeof app;

クライアント側​

import { hc } from 'hono/client';
import type { AppType } from '../../../server';

export const client = hc<AppType>('https://api.example.com');

const { data } = useQuery({
queryKey: ['todos'],
queryFn: async () => {
const res = await client.todos.$get();
return res.json(); // 戻り値が完全に型付き
},
});

フォーム管理​

React Hook Form + Zodの組み合わせをそのまま使用できる。

import { useForm, Controller } from "react-hook-form";
import { zodResolver } from "@hookform/resolvers/zod";
import { z } from "zod";
import { TextInput, Text, Button, View } from "react-native";

const schema = z.object({
email: z.email("メールアドレスの形式が正しくありません"),
password: z.string().min(8, "8文字以上で入力してください"),
});

type FormData = z.infer<typeof schema>;

export function SignInForm() {
const {
control,
handleSubmit,
formState: { errors },
} = useForm<FormData>({
resolver: zodResolver(schema),
});

const onSubmit = (data: FormData) => {
console.log(data);
};

return (
<View>
<Controller
control={control}
name="email"
render={({ field: { onChange, onBlur, value, ref } }) => (
<TextInput
ref={ref}
value={value}
onChangeText={onChange}
onBlur={onBlur}
placeholder="メールアドレス"
autoCapitalize="none"
keyboardType="email-address"
/>
)}
/>
{errors.email && <Text>{errors.email.message}</Text>}
<Button title="ログイン" onPress={handleSubmit(onSubmit)} />
</View>
);
}

スタイリング​

NativeWind​

Tailwind CSSのutility-first書き方をReact Nativeで実現する。

import { Text, View } from 'react-native';

export function Card() {
return (
<View className="rounded-2xl bg-white p-4 shadow-sm">
<Text className="text-lg font-bold text-zinc-900">
React Native、だいぶ優しくなったね
</Text>
<Text className="mt-2 text-sm text-zinc-600">
でもAndroidの端末差分はまだたまに牙をむく
</Text>
</View>
);
}

gluestack-ui​

shadcn/ui流のコピペ方式でコンポーネントを管理する。

# プロジェクト初期化
npx gluestack-ui@latest init

# コンポーネント追加
npx gluestack-ui@latest add button

コードがcomponents/ui/以下に生成されるため、自由にカスタマイズできる。

ローカルストレージ​

MMKV(react-native-mmkv)を使用する。AsyncStorageより約30倍高速で、同期的に読み書きできる。

import { createMMKV } from 'react-native-mmkv';

export const storage = createMMKV();

// awaitなしで同期的に使える
storage.set('onboardingDone', true);
const done = storage.getBoolean('onboardingDone');

Zustandのpersistミドルウェアと組み合わせると、設定値を永続化できる。

バックエンドと認証​

個人開発や小中規模プロダクトでは、Supabaseが有力候補である。

import 'react-native-url-polyfill/auto';
import { createClient } from '@supabase/supabase-js';
import 'expo-sqlite/localStorage/install';

const supabaseUrl = process.env.EXPO_PUBLIC_SUPABASE_URL!;
const supabasePublishableKey =
process.env.EXPO_PUBLIC_SUPABASE_PUBLISHABLE_KEY!;

export const supabase = createClient(supabaseUrl, supabasePublishableKey, {
auth: {
storage: localStorage,
autoRefreshToken: true,
persistSession: true,
detectSessionInUrl: false,
},
});

// メール・パスワード認証
const { error } = await supabase.auth.signInWithPassword({ email, password });

推奨ディレクトリ構成​

機能単位で整理する構成を推奨する。

my-rn-app/
├── app/ # Expo Router(ルーティング)
│ ├── _layout.tsx # ルートレイアウト
│ ├── +not-found.tsx # 404相当
│ ├── (auth)/ # 認証画面
│ │ ├── sign-in.tsx
│ │ └── sign-up.tsx
│ └── (tabs)/ # タブ画面
│ ├── _layout.tsx
│ ├── index.tsx
│ └── settings.tsx
├── assets/ # 静的リソース
│ ├── images/
│ └── fonts/
├── src/
│ ├── features/ # 機能単位で縦に切る
│ │ └── todo/
│ │ ├── api.ts # API呼び出し
│ │ ├── hooks.ts # TanStack Query hooks
│ │ ├── schema.ts # Zodスキーマ
│ │ └── components/ # 機能専用UI
│ ├── shared/ # 機能をまたぐ共通レイヤ
│ │ ├── api/
│ │ │ ├── client.ts # APIクライアント
│ │ │ └── queryClient.ts # TanStack Query設定
│ │ ├── components/
│ │ │ └── ui/ # gluestack-ui
│ │ ├── lib/
│ │ │ ├── supabase.ts
│ │ │ └── storage.ts # MMKVラッパー
│ │ ├── styles/
│ │ │ └── theme.ts
│ │ └── utils/
│ └── stores/ # Zustand
│ └── useSettingsStore.ts
├── app.json # Expo設定
├── eas.json # EASビルド設定
├── global.css # NativeWind CSS
└── metro.config.js # Metro設定

設計原則​

  1. app/は画面の置き場に徹する: ロジックはsrc/features/へ配置
  2. 依存の方向は一方通行: features/ → shared/のimportはOK、逆は禁止
  3. 機能単位で縦に切る: api.ts/hooks.ts/schema.tsを機能ごとに揃える

まとめ​

2026年のReact Native開発環境は、以下の点でWeb開発者に優しくなっている。

  • TypeScript、TanStack Query、Zustand、React Hook Form、ZodなどWeb開発の定番ライブラリがそのまま使える
  • Expo RouterによりNext.js App Routerに似たファイルベース開発が可能
  • NativeWindでTailwind CSSの書き味を再現
  • gluestack-uiでshadcn/uiと同じ開発フローを実現
  • EASによりビルド・配信・更新の「行政手続き」が大幅に簡略化

既存のWeb開発知識を活かしながら、モバイルアプリ開発に参入できる環境が整っている。

参考リンク​