React Nativeでモバイルアプリを開発する際、カメラ機能の組み込みは頻出要件の一つです。しかし、かつて定番だったreact-native-cameraは2023年6月にアーカイブされ、現在はメンテナンスが停止しています。2026年現在、React Nativeのカメラライブラリはexpo-cameraとreact-native-vision-cameraの2つが主流となっています。
この記事では、両ライブラリの機能差・パフォーマンス特性・導入手順を実際のコード付きで整理し、プロジェクトの要件に応じた最適な選択ができるようにまとめています。
React Nativeで使えるカメラライブラリの全体像
2026年2月時点で、React Nativeアプリにカメラ機能を追加する主な選択肢は3つあります。
| 項目 | expo-camera | react-native-vision-camera | react-native-camera-kit |
|---|---|---|---|
| 最新バージョン | v55.0.6(SDK 55対応) | v4.7.3 | v17.0.1 |
| メンテナー | Expo公式チーム | Marc Rousavy | Tesla Motors |
| npmダウンロード/週 | 約53〜65万 | 約46万 | 約3万 |
| GitHub Stars | Expoリポジトリに内包(47k+) | 約9,200 | 約2,700 |
| 対応プラットフォーム | iOS / Android / Web | iOS / Android | iOS / Android |
| Expo Goでの動作 | 対応 | 非対応(dev client必須) | 非対応 |
| フレームプロセッサ | 非対応 | 対応(Worklet / Skia連携) | 非対応 |
| QR/バーコード読み取り | 対応 | 対応 | 対応 |
| 動画撮影 | 対応 | 対応 | 非対応 |
| ライセンス | MIT | MIT | MIT |
旧react-native-cameraは2020年に非推奨の議論が始まり、2023年6月にGitHubリポジトリがアーカイブされました(出典: GitHub)。現在このライブラリを使用中のプロジェクトは、react-native-vision-cameraまたはexpo-cameraへの移行が推奨されています。
expo-camera:Expoプロジェクトに最適な標準カメラ
expo-cameraの特徴
expo-cameraはExpo SDKに含まれる公式カメラライブラリです。CameraViewコンポーネントを中心としたシンプルなAPIが特徴で、権限管理から写真撮影・動画録画・バーコードスキャンまで一通りの機能を提供します。
SDK 52(2024年11月)で旧Cameraコンポーネント(expo-camera/legacy)が完全に廃止され、CameraViewが唯一のAPIになりました。SDK 55(2026年1月)ではパッケージのメジャーバージョンがSDKバージョンと統一される新しい命名規則が導入されています(出典: Expo公式ドキュメント)。
expo-cameraの主な機能
- 写真撮影(EXIF・Base64対応)
- 動画録画(録画時間・ファイルサイズ制限指定可能)
- フロント/バックカメラ切り替え
- フラッシュ・トーチ制御
- ピンチズーム
- バーコード/QRコードスキャン(
onBarcodeScannedコールバックまたはlaunchScanner()モーダル) - 動画スタビライゼーション(SDK 55で追加)
- Androidフロントカメラでのスクリーンフラッシュ(SDK 55で追加)
セットアップ手順
# Expoプロジェクトにインストール
npx expo install expo-camera
app.jsonにプラグイン設定を追加します。
{
"plugins": [
[
"expo-camera",
{
"cameraPermission": "カメラへのアクセスを許可してください",
"microphonePermission": "マイクへのアクセスを許可してください",
"recordAudioAndroid": true
}
]
]
}
写真撮影の実装例
import { useRef, useState } from 'react';
import { View, Button, Image, StyleSheet } from 'react-native';
import { CameraView, useCameraPermissions } from 'expo-camera';
export default function PhotoScreen() {
const cameraRef = useRef<CameraView>(null);
const [permission, requestPermission] = useCameraPermissions();
const [photoUri, setPhotoUri] = useState<string | null>(null);
if (!permission) return null;
if (!permission.granted) {
return (
<View style={styles.container}>
<Button title="カメラを許可する" onPress={requestPermission} />
</View>
);
}
const takePhoto = async () => {
if (!cameraRef.current) return;
const photo = await cameraRef.current.takePictureAsync({
quality: 0.8,
exif: true,
});
if (photo) setPhotoUri(photo.uri);
};
return (
<View style={styles.container}>
{photoUri ? (
<Image source={{ uri: photoUri }} style={styles.preview} />
) : (
<CameraView ref={cameraRef} style={styles.camera} facing="back">
<View style={styles.overlay}>
<Button title="撮影" onPress={takePhoto} />
</View>
</CameraView>
)}
</View>
);
}
const styles = StyleSheet.create({
container: { flex: 1 },
camera: { flex: 1 },
preview: { flex: 1 },
overlay: {
flex: 1,
justifyContent: 'flex-end',
alignItems: 'center',
paddingBottom: 40,
},
});
ポイント: CameraViewは画面上に同時に1つしか表示できません。画面遷移時には必ずアンマウントしてください。
react-native-vision-camera:高度なカメラ制御が必要な場合の選択肢
VisionCameraの特徴
react-native-vision-camera(以下VisionCamera)は、Marc Rousavy氏が開発・メンテナンスしているライブラリです。カメラのハードウェアに近いレベルでの制御が可能で、フレームプロセッサと呼ばれるリアルタイムフレーム処理機能が最大の差別化要素です。
v4系ではAndroid側がCameraXベースに全面書き換えされ、安定性が大きく向上しました。最新のv4.7.3(2025年11月リリース)では16KBページサイズのサポートやGS1 DataBarスキャン対応が追加されています(出典: GitHub)。
VisionCameraの主な機能
- 写真撮影・動画録画
- QR/バーコードスキャン
- フレームプロセッサ(JSワークレットでリアルタイム画像処理)
- Skiaフレームプロセッサ(カメラフレーム上にテキスト・図形・シェーダーを直接描画)
- マルチカメラデバイス対応(広角・望遠・超広角の切り替え)
- カスタム解像度・FPS設定(30〜240fps)
- HDR・ナイトモード
- コミュニティプラグイン(顔検出、テキスト認識OCR、ポーズ検出など)
セットアップ手順
# bare React Native / Expo dev clientプロジェクトにインストール
npm install react-native-vision-camera
# iOSの場合
cd ios && pod install
iOS権限設定(Info.plist)
<key>NSCameraUsageDescription</key>
<string>カメラへのアクセスを許可してください</string>
<key>NSMicrophoneUsageDescription</key>
<string>マイクへのアクセスを許可してください</string>
Android権限設定(AndroidManifest.xml)
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />
写真撮影の実装例
import { useRef, useCallback } from 'react';
import { View, Button, StyleSheet } from 'react-native';
import {
Camera,
useCameraDevice,
useCameraPermission,
} from 'react-native-vision-camera';
export default function VisionPhotoScreen() {
const cameraRef = useRef<Camera>(null);
const device = useCameraDevice('back');
const { hasPermission, requestPermission } = useCameraPermission();
const takePhoto = useCallback(async () => {
if (!cameraRef.current) return;
const photo = await cameraRef.current.takePhoto({
flash: 'auto',
qualityPrioritization: 'balanced',
});
console.log('保存先:', photo.path);
}, []);
if (!hasPermission) {
return (
<View style={styles.container}>
<Button title="カメラを許可する" onPress={requestPermission} />
</View>
);
}
if (!device) return null;
return (
<View style={styles.container}>
<Camera
ref={cameraRef}
style={styles.camera}
device={device}
isActive={true}
photo={true}
/>
<View style={styles.overlay}>
<Button title="撮影" onPress={takePhoto} />
</View>
</View>
);
}
const styles = StyleSheet.create({
container: { flex: 1 },
camera: { flex: 1 },
overlay: {
position: 'absolute',
bottom: 40,
alignSelf: 'center',
},
});
フレームプロセッサの活用例
VisionCamera最大の特徴であるフレームプロセッサを使うと、カメラのフレームごとにJavaScript(Worklet)を実行できます。顔認識やOCRなどの処理をリアルタイムで行う場合に活用します。
import { Camera, useFrameProcessor } from 'react-native-vision-camera';
import { useFaceDetector } from 'react-native-vision-camera-face-detector';
function FaceDetectionScreen() {
const device = useCameraDevice('front');
const { detectFaces } = useFaceDetector();
const frameProcessor = useFrameProcessor((frame) => {
'worklet';
const faces = detectFaces(frame);
if (faces.length > 0) {
console.log(`検出された顔: ${faces.length}`);
}
}, [detectFaces]);
if (!device) return null;
return (
<Camera
style={{ flex: 1 }}
device={device}
isActive={true}
frameProcessor={frameProcessor}
/>
);
}
QRコード・バーコード読み取りの実装
カメラアプリでよく求められるQRコード読み取り機能を、両ライブラリで実装する方法を比較します。
expo-cameraでのQRコード読み取り
import { useState } from 'react';
import { View, Text, StyleSheet } from 'react-native';
import { CameraView, useCameraPermissions } from 'expo-camera';
export default function ExpoQRScanner() {
const [permission, requestPermission] = useCameraPermissions();
const [scannedData, setScannedData] = useState<string | null>(null);
if (!permission?.granted) {
return <Button title="カメラを許可する" onPress={requestPermission} />;
}
return (
<View style={styles.container}>
<CameraView
style={styles.camera}
facing="back"
barcodeScannerSettings={{
barcodeTypes: ['qr', 'ean13', 'code128'],
}}
onBarcodeScanned={(result) => {
setScannedData(result.data);
}}
/>
{scannedData && (
<View style={styles.result}>
<Text>読み取り結果: {scannedData}</Text>
</View>
)}
</View>
);
}
const styles = StyleSheet.create({
container: { flex: 1 },
camera: { flex: 1 },
result: {
position: 'absolute',
bottom: 60,
alignSelf: 'center',
backgroundColor: 'rgba(255,255,255,0.9)',
padding: 16,
borderRadius: 8,
},
});
VisionCameraでのQRコード読み取り
VisionCameraではv4からcodeScannerプロパティが組み込みで提供されています。
import { useCallback } from 'react';
import { View, Text, StyleSheet } from 'react-native';
import {
Camera,
useCameraDevice,
useCodeScanner,
} from 'react-native-vision-camera';
export default function VisionQRScanner() {
const device = useCameraDevice('back');
const codeScanner = useCodeScanner({
codeTypes: ['qr', 'ean-13', 'code-128'],
onCodeScanned: (codes) => {
if (codes.length > 0) {
console.log('読み取り:', codes[0].value);
}
},
});
if (!device) return null;
return (
<View style={styles.container}>
<Camera
style={styles.camera}
device={device}
isActive={true}
codeScanner={codeScanner}
/>
</View>
);
}
const styles = StyleSheet.create({
container: { flex: 1 },
camera: { flex: 1 },
});
QRコードスキャン機能の比較
| 比較ポイント | expo-camera | VisionCamera |
|---|---|---|
| 導入の手軽さ | onBarcodeScannedプロパティのみで完結 | useCodeScannerフックを使用 |
| モーダル表示 | launchScanner()で専用UIを起動可能 | 自前で実装が必要 |
| スキャン精度 | 標準的 | 高精度(GS1 DataBar対応) |
| 複数コード同時読み取り | 1コードずつ | 複数コード同時対応 |
| Expo Goでの動作 | 対応 | 非対応 |
動画撮影の実装
expo-cameraでの動画撮影
import { useRef, useState } from 'react';
import { View, Button, StyleSheet } from 'react-native';
import { CameraView, useCameraPermissions } from 'expo-camera';
export default function ExpoVideoRecorder() {
const cameraRef = useRef<CameraView>(null);
const [permission, requestPermission] = useCameraPermissions();
const [isRecording, setIsRecording] = useState(false);
if (!permission?.granted) {
return <Button title="カメラを許可する" onPress={requestPermission} />;
}
const toggleRecording = async () => {
if (!cameraRef.current) return;
if (isRecording) {
cameraRef.current.stopRecording();
setIsRecording(false);
} else {
setIsRecording(true);
const video = await cameraRef.current.recordAsync({
maxDuration: 60,
});
if (video) console.log('動画URI:', video.uri);
}
};
return (
<View style={styles.container}>
<CameraView
ref={cameraRef}
style={styles.camera}
facing="back"
mode="video"
/>
<View style={styles.overlay}>
<Button
title={isRecording ? '停止' : '録画開始'}
onPress={toggleRecording}
/>
</View>
</View>
);
}
const styles = StyleSheet.create({
container: { flex: 1 },
camera: { flex: 1 },
overlay: {
position: 'absolute',
bottom: 40,
alignSelf: 'center',
},
});
VisionCameraでの動画撮影
import { useRef, useState, useCallback } from 'react';
import { View, Button, StyleSheet } from 'react-native';
import { Camera, useCameraDevice } from 'react-native-vision-camera';
export default function VisionVideoRecorder() {
const cameraRef = useRef<Camera>(null);
const device = useCameraDevice('back');
const [isRecording, setIsRecording] = useState(false);
const startRecording = useCallback(() => {
if (!cameraRef.current) return;
setIsRecording(true);
cameraRef.current.startRecording({
flash: 'off',
onRecordingFinished: (video) => {
console.log('保存先:', video.path);
setIsRecording(false);
},
onRecordingError: (error) => {
console.error('録画エラー:', error);
setIsRecording(false);
},
});
}, []);
const stopRecording = useCallback(() => {
cameraRef.current?.stopRecording();
}, []);
if (!device) return null;
return (
<View style={styles.container}>
<Camera
ref={cameraRef}
style={styles.camera}
device={device}
isActive={true}
video={true}
audio={true}
/>
<View style={styles.overlay}>
<Button
title={isRecording ? '停止' : '録画開始'}
onPress={isRecording ? stopRecording : startRecording}
/>
</View>
</View>
);
}
const styles = StyleSheet.create({
container: { flex: 1 },
camera: { flex: 1 },
overlay: {
position: 'absolute',
bottom: 40,
alignSelf: 'center',
},
});
プロジェクト要件から選ぶカメラライブラリ
どのライブラリを選ぶかは、プロジェクトの技術構成と要件で決まります。
expo-cameraを選ぶべきケース
- Expo管理ワークフロー(Managed Workflow)を利用している
- Expo Goで手軽に動作確認したい
- 基本的な写真撮影・動画録画・バーコードスキャンで十分
- Webプラットフォームにも対応させたい
- セットアップの手軽さを優先したい
VisionCameraを選ぶべきケース
- リアルタイムの画像処理やML推論が必要(顔認識、OCR、物体検出など)
- カメラのFPS・解像度・HDRなどを細かく制御したい
- 複数カメラデバイス(広角・望遠)を使い分けたい
- Skia連携でカメラ映像にリアルタイムエフェクトをかけたい
- パフォーマンスが最優先のカメラアプリを開発している
react-native-camera-kitを選ぶべきケース
- シンプルなカメラ機能だけが必要で、依存関係を最小限に抑えたい
- iOSシミュレータでのカメラプレビュー確認が必要
- Tesla Motorsがメンテナンスしている安定性を重視する場合
判断フローチャート
Expoプロジェクト?
├── Yes → フレームプロセッサが必要?
│ ├── Yes → VisionCamera(Expo dev clientで利用)
│ └── No → expo-camera
└── No → 高度なカメラ制御が必要?
├── Yes → VisionCamera
└── No → react-native-camera-kit or VisionCamera
よくある問題と解決策
カメラのプレビューが真っ黒になる
expo-cameraの場合、CameraViewを同時に2つ以上マウントすると片方が真っ黒になります。画面遷移時に前の画面のカメラをアンマウントしてください。VisionCameraではisActiveプロパティをfalseにすることで同様の問題を回避できます。
Androidでのビルドエラー
VisionCamera v4.6.1以降でreact-native@0.76以降を使用する場合、CameraXの重複ライブラリエラーが発生することがあります。android/build.gradleでCameraXのバージョンを明示的に指定してください。
// android/build.gradle
ext {
cameraxVersion = "1.5.0-alpha01"
}
Expo Goでカメラが動作しない
VisionCameraはExpo Goでは動作しません。Expo環境でVisionCameraを使う場合は、開発ビルド(dev client)が必要です。
npx expo run:ios
# または
npx expo run:android
Expo Goのみで開発を完結させたい場合は、expo-cameraを選択してください。
権限リクエストが表示されない
iOS・Android共通で、権限の説明文(NSCameraUsageDescription等)が未設定だとアプリがクラッシュします。expo-cameraではapp.jsonのプラグイン設定、VisionCameraではInfo.plistとAndroidManifest.xmlへの記述をそれぞれ忘れずに行ってください。
react-native-cameraからの移行
旧react-native-cameraを使用中のプロジェクトは、以下の対応が必要です。
react-native-cameraをアンインストール- VisionCameraまたはexpo-cameraをインストール
RNCameraコンポーネントを新しいAPI(CameraまたはCameraView)に書き換え- 権限管理コードをフック(
useCameraPermission/useCameraPermissions)に置き換え - バーコードスキャンのコールバック形式を新APIに合わせる
まとめ
React Nativeのカメラライブラリ選定は、プロジェクトの技術スタックと求められるカメラ機能の深さで決まります。
- expo-cameraは、Expoエコシステム内での手軽な導入と、写真撮影・動画録画・バーコード読み取りといった基本機能を安定して提供します。SDK 55でバージョニングが刷新され、動画スタビライゼーションなど新機能も追加されています。
- react-native-vision-cameraは、フレームプロセッサによるリアルタイム画像処理やSkia連携、マルチカメラ制御など、高度なカメラアプリ開発に必要な機能を網羅しています。Expo dev clientとの組み合わせでExpoプロジェクトでも利用可能です。
どちらのライブラリもMITライセンスで提供されており、活発にメンテナンスされています。まずはプロジェクトの要件を整理し、上記の判断基準に沿って最適なライブラリを選択してください。
