React Nativeでモバイルアプリを開発する際、カメラ機能の組み込みは頻出要件の一つです。しかし、かつて定番だったreact-native-cameraは2023年6月にアーカイブされ、現在はメンテナンスが停止しています。2026年現在、React Nativeのカメラライブラリはexpo-camerareact-native-vision-cameraの2つが主流となっています。

この記事では、両ライブラリの機能差・パフォーマンス特性・導入手順を実際のコード付きで整理し、プロジェクトの要件に応じた最適な選択ができるようにまとめています。

React Nativeで使えるカメラライブラリの全体像

2026年2月時点で、React Nativeアプリにカメラ機能を追加する主な選択肢は3つあります。

項目expo-camerareact-native-vision-camerareact-native-camera-kit
最新バージョンv55.0.6(SDK 55対応)v4.7.3v17.0.1
メンテナーExpo公式チームMarc RousavyTesla Motors
npmダウンロード/週約53〜65万約46万約3万
GitHub StarsExpoリポジトリに内包(47k+)約9,200約2,700
対応プラットフォームiOS / Android / WebiOS / AndroidiOS / Android
Expo Goでの動作対応非対応(dev client必須)非対応
フレームプロセッサ非対応対応(Worklet / Skia連携)非対応
QR/バーコード読み取り対応対応対応
動画撮影対応対応非対応
ライセンスMITMITMIT

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-cameraVisionCamera
導入の手軽さ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.plistAndroidManifest.xmlへの記述をそれぞれ忘れずに行ってください。

react-native-cameraからの移行

react-native-cameraを使用中のプロジェクトは、以下の対応が必要です。

  1. react-native-cameraをアンインストール
  2. VisionCameraまたはexpo-cameraをインストール
  3. RNCameraコンポーネントを新しいAPI(CameraまたはCameraView)に書き換え
  4. 権限管理コードをフック(useCameraPermission/useCameraPermissions)に置き換え
  5. バーコードスキャンのコールバック形式を新APIに合わせる

まとめ

React Nativeのカメラライブラリ選定は、プロジェクトの技術スタックと求められるカメラ機能の深さで決まります。

  • expo-cameraは、Expoエコシステム内での手軽な導入と、写真撮影・動画録画・バーコード読み取りといった基本機能を安定して提供します。SDK 55でバージョニングが刷新され、動画スタビライゼーションなど新機能も追加されています。
  • react-native-vision-cameraは、フレームプロセッサによるリアルタイム画像処理やSkia連携、マルチカメラ制御など、高度なカメラアプリ開発に必要な機能を網羅しています。Expo dev clientとの組み合わせでExpoプロジェクトでも利用可能です。

どちらのライブラリもMITライセンスで提供されており、活発にメンテナンスされています。まずはプロジェクトの要件を整理し、上記の判断基準に沿って最適なライブラリを選択してください。