2026年のためのExpoイメージピッカー完全ガイド
UIが完成し、プロフィール画面に「アップロード写真」ボタンが追加されましたが、簡単な部分が突然簡単ではなくなりました。実際の画像選択フローは、ネイティブパーミッション、OS制御インターフェイス、開発時詳細など、開発者が期待するよりも多くの形態を取ります。 Expo Image Pickerは、公式のExpoライブラリで、デバイスライブラリから画像や動画を選択したり、カメラで写真を撮ったりするシステムUIを開くものです。Expoパッケージリポジトリで説明されているように、. Expo の実装では、信頼性の高いネイティブ メディア入力へのブリッジが得られますが、すべてのデバイスで同様に振る舞うカスタム メディア エクスペリエンスは得られません。
このガイドは、デモではなく最初の実装用に書かれています。生産環境で重要な決定に焦点を当て、後で驚かれないようにするパーミッション ハンドリング、安全な結果のパース、ユーザーがファイルを選択した後の上ロード パターンを含みます。カスタム ネイティブ セットアップで作業している場合、この実装が Expo 開発クライアント ワークフローとどのように異なるかを理解することも役立ちます。 Expo 開発クライアント ワークフロー.
Expo イメージ ピッカーの使い方
エクスポ イメージ ピッカーの使い方
プロフィール写真の要求が来ます。1週間後、同じ機能が受領書のアップロード、事故報告のカメラキャプチャ、ユーザーが最初の許可を拒否したときのリトライも必要になります。画像入力は、ネイティブの許可、OS所有のUI、テンポラリファイルの処理、バックエンドのアップロードフローに触れるため、速く拡大します。
expo-image-picker エクスポのSDKモジュールはその仕事に適しています。プラットフォームピッカーまたはカメラUIを開き、選択されたメディアをReact Nativeのcodeが扱える形で返します。JavaScriptのAPIは小さく、主な課題は、両方のマネージドとバーレスプロジェクトでネイティブのセットアップ、許可フロー、結果の処理を正しく行うことです。
主なトレードオフは簡単です。iOSとAndroidが独自のメディアUIを提示するのを許可するのではなく、カスタムピッカーをJavaScriptで構築するのを避けるのです。通常、結果は良好です:ユーザーはシステムスクリーンをすでに理解しているため、許可の促し方はOSの期待どおりに動作し、チームはJavaScriptでギャラリーコードを維持する必要がありません。
この機能をネイティブの統合機能として扱ってください。Reactインターフェイスがあります。
この考え方は役に立ちます。失敗モードは、ピッカーを呼び出すボタンではなく、ほとんどの場合、次の3つの場所から来ます:
- ネイティブの設定: プラグインのセットアップが不足している、許可文字列が不正、またはビルドが古い状態で変更された設定
- 実行時挙動: ユーザーが許可を拒否したり、iOSでライブラリへの制限された許可を付与したり、選択なしでフローをキャンセルしたりします。
- 結果の解釈: 現在のAPIは
assets配列なので、古い例が直接読めなくなるresult.uri失敗する
ワークフローの選択もセットアップパスを変える。マネージドのExpoアプリでは、ほとんどのネイティブの作業はアプリの設定ファイルにあり、設定が変更されたときに再構築が必要になる。ベアアプリでは、ExpoモジュールAPIがまだ利用できるが、iOSとAndroidのプロジェクト設定を直接確認する必要がある。チームがカスタムクライアントを使用している場合、このガイドはCapgoの説明と組み合わせて、Expo開発クライアントがネイティブモジュールのテストをどのように変えるかを理解するのに役立つ。 ワークフローの分割は、このガイドの残りの部分で重要な点である。ハッピーパスは半分の話だけである。ピッカー実装は、両方のワークフローで動作し、プラットフォーム固有のパーミッションの奇妙さに驚かず、ユーザーに予期せぬ動作をせずに、ローカルプレビューで止まらずに、利用可能なファイルをアップロード層に渡すことができる。.
インストールと基本設定
インストールには1つのコマンドが必要。ネイティブの設定が正しく設定されていれば、実機で、カスタム開発クライアントで、プロダクションビルドでもピッカーが動作する。
Expoのバージョンに応じたインストーラーから始める。
expo-image-picker gives a React-facing API over the platform pickers for photos, videos, and camera capture. The JavaScript call is simple. The setup is not, because photo access and camera access are controlled by iOS and Android, not by React Native.

Expoのバージョンに応じたインストーラーから始める。
npx expo install expo-image-picker
代わりに expo install instead of npm install または yarn addエクスポはパッケージバージョンをあなたのSDKに合わせます。これにより、ネイティブの互換性の問題の一般的なクラスを回避できます。エクスポモジュールがリリースプロセスにどのようにフィットするかを比較する場合、この エクスポツールの概要 管理されたワークフローの設定
管理されたワークフローでは、App Configでプラグインを宣言し、エクスポがビルド時に出力するネイティブの変更を適用する
例
最小限の設定です。実際には、iOSではシステムのプロンプトがアクセスが必要な理由を説明する必要があるため、チームは許可テキストを追加することが多いです。ユーザーが実行するアクションに合わせて、テキストを具体的にします。例えば、「プロフィール写真をアップロードする」は「メディアアクセスが必要」より良いです。 app.json:
{
"expo": {
"plugins": ["expo-image-picker"]
}
}
1 つの運用上の詳細が多くの時間を浪費しています。パーミッション文字列、ネイティブの設定、またはその他の設定を変更すると、再ビルドが必要になります。JavaScriptを再読み込みしても、変更が適用されません。エクスポゴでは、クライアントがすでに含まれているものに制限されています。開発ビルドまたはプロダクションビルドの場合、ネイティブのプロジェクトはあなたの設定を反映するのは、新しいビルド後にのみ
バレなReact Nativeの設定の詳細 pluginsバレなアプリでは、パッケージ__CAPGO_KEEP_0__は同じですが、ネイティブのプロジェクトを自分で確認する必要があります。iOSの使用説明は最初に確認するべきです。ライブラリを開く、カメラを開く、または音声付きのビデオを記録することができるフローがある場合、対応するパーミッション文字列が必要です
アプリフローのプラグインまたは
エクスポのパッケージバージョンをあなたのAPIに合わせることで、ネイティブの互換性の問題の一般的なクラスを回避できます。 Info.plist 再構築する前に。
バーレプロジェクトの実用的なチェックリストは次のようになります。
- インストール
expo-image-pickerとnpx expo install expo-image-picker. - プロジェクトがExpoの設定プラグインを使用している場合にのみ、プラグインの設定を追加します。
- iOSの使用説明が機能に一致していることを確認します。
- iOSおよびAndroidアプリを再構築する必要があります。ネイティブの設定が変更された後。
Missing permission text often looks like a runtime bug because the UI code is fine and the button handler runs. The failure is lower in the stack. I usually check Info.plist, the app config, and whether the current build includes the latest native changes before I touch the component code.
設定を予測可能にするために、習慣的な習慣があります。
- 実際のアクションのために許可のテキストを書きます。 ユーザーは、提示されたプロンプトの理由を理解する必要があります。
- カメラとライブラリを個別に設定します: 一方が機能しない場合もう一方が機能することがあります。
- ネイティブの変更後は再構築します: ホットリロードと高速リフレッシュはネイティブの権限を更新しません。
- デバイスでテストします: シミュレータの動作は権限やカメラの問題を隠すことができます。
開発中は正常に動作するが、テストフライトまたはPlayストアのビルドで動作が崩れる場合は、まずは設定問題とみなしてください。ほとんどの場合、それは事実です。
カメラとメディアライブラリのアクセス
ユーザーが「写真をアップロードする」ボタンをタップすると、カメラまたはライブラリが開くことを期待し、ユーザーはその時点でアプリの1つの仕事があります。システムUIを開き、拒否またはキャンセルを処理することなく画面を破壊せずに、プレビューまたはアップロード用に利用可能なローカルファイル参照を返します。
That sounds simple until you test both managed and bare builds across iOS and Android. The JavaScript API stays compact, but the runtime behavior still depends on OS prompts, device hardware, and how your native permissions were configured earlier.

__CAPGO_KEEP_0__
Expo管理とバレーウォークフローのプロジェクトでは、コアフローは両方とも一貫しています。許可された権限を要求し、ピッカーを起動し、ユーザーがキャンセルしたかどうかを確認し、最初のアセットを読み取ります。 result.assets.
ベースラインコンポーネントは次のようになります。
import { useState } from 'react';
import { View, Button, Image, Alert } from 'react-native';
import * as ImagePicker from 'expo-image-picker';
export default function PhotoInput() {
const [imageUri, setImageUri] = useState<string | null>(null);
const pickFromLibrary = async () => {
const permission = await ImagePicker.requestMediaLibraryPermissionsAsync();
if (!permission.granted) {
Alert.alert('Permission required', 'Please allow photo library access.');
return;
}
const result = await ImagePicker.launchImageLibraryAsync({
mediaTypes: ['images'],
allowsEditing: true,
quality: 1,
});
if (result.canceled) return;
const asset = result.assets?.[0];
if (!asset?.uri) return;
setImageUri(asset.uri);
};
const takePhoto = async () => {
const permission = await ImagePicker.requestCameraPermissionsAsync();
if (!permission.granted) {
Alert.alert('Permission required', 'Please allow camera access.');
return;
}
const result = await ImagePicker.launchCameraAsync({
allowsEditing: true,
quality: 1,
});
if (result.canceled) return;
const asset = result.assets?.[0];
if (!asset?.uri) return;
setImageUri(asset.uri);
};
return (
<View>
<Button title="Choose from library" onPress={pickFromLibrary} />
<Button title="Take photo" onPress={takePhoto} />
{imageUri ? (
<Image
source={{ uri: imageUri }}
style={{ width: 200, height: 200 }}
/>
) : null}
</View>
);
}
ここで3つの詳細な点が重要です。
- ライブラリとカメラのパーミッションを個別に要求する必要があります。どちらも独立して失敗します。
- キャンセルを正常なユーザーアクションとして扱い、エラーステートとして扱うのではなく。
- から読み取ります。
assets[0],uri.
Library and camera flows
ライブラリとカメラのフロー
The camera path has more ways to fail in development. iOS Simulator support is limited. Android emulators may not expose camera behavior that matches a real device. In bare projects, those gaps can send you looking at component code even though the actual issue is native config or test environment.
開発環境ではカメラパスが失敗する方法が多くあります。iOSシミュレーターのサポートは限られています。Androidエミュレータでは、実機と同じカメラの動作を表現できない場合があります。バレーアプリでは、実際の問題はネイティブの設定またはテスト環境である可能性があります。コンポーネントAPIを確認することなく、問題を解決するのに時間がかかります。
const showPickerOptions = () => {
Alert.alert('Upload image', 'Choose a source', [
{ text: 'Camera', onPress: takePhoto },
{ text: 'Photo Library', onPress: pickFromLibrary },
{ text: 'Cancel', style: 'cancel' },
]);
};
ユーザーからソースを要求するUIパターンは、ピッカーを呼び出す前に次のようになります:__CAPGO_KEEP_0__
あなたのアプリがエクスポ以外のファイルアクセスパターンをサポートしている場合、またはネイティブスタック間のコンベンションを比較している場合、この Capacitor 写真ライブラリの参照
は便利なコンテキストです。
短いデモは、チームメンバーまたはQAにこのフローを示すときに役立ちます。
expo-image-picker システムUIの期待
プラットフォームピッカーまたはカメラUIを開きます。アプリはそのフローの全ての画面を制御していません。その区別は重要です。 “私のデバイスで動作する”ということはよくありますが、 “OSが許可したパスでテストした”ということです。
iOSでは、ユーザーはライブラリへの限定アクセスを許可する代わりに、完全なアクセスを許可することができます。Androidでは、ピッカーの動作はOSバージョンとベンダースキンによって異なります。マネージドワークフロープロジェクトでは、エクスポはネイティブのワイヤリングの多くを取り扱います。ベアワークフロープロジェクトでは、ネイティブの許可変更が含まれるビルドされたアプリを確認する必要があります。JavaScriptの呼び出しサイトは両方のケースで同じままですが、実行時結果は異なります。
- 私はこれらのケースをテストする前に、機能を完了する前にします。
- 最初の許可要求
- 許可が拒否された
- ユーザーのキャンセル
- 物理デバイスでのカメラキャプチャの成功
- 返されたローカルURIの即時プレビュー
これらは実際の生産動作に直接対応するケースです。また、ファイルをサーバー、モデレーションパイプライン、または公開エンドポイント(例えばインスタグラムのメディアパブリッシング__CAPGO_KEEP_0__)に送信する必要がある場合、次のステップをきちんと設定します。 インスタグラムのメディアパブリッシングAPI.
ピッカー結果とオプションのハンドリング
ピッカー結果は、通常、実際の生産ロジックが必要です。システムUIは構造化されたオブジェクトを返しますが、ファイルパスだけではありません。ここで小さなミスが原因で、キャンセルしたときにユーザーがクラッシュしたり、空のアップロードをしたりすることがあります。
結果オブジェクトを正しく読み取る
現在のExpoアプリでは重要なのは結果の形状であり、 result.assets[0].uriのトップレベル result.uriです。ここでの詳細は、管理されたワークフローとベアワークフロープロジェクトの両方に影響を与えます。JavaScriptAPIは同じですが、ネイティブのセットアップは下部で異なります。
guard-firstパターンを使用してください:
const result = await ImagePicker.launchImageLibraryAsync({
mediaTypes: ['images'],
allowsEditing: true,
quality: 1,
});
if (result.canceled) {
return;
}
const asset = result.assets?.[0];
if (!asset) {
return;
}
const { uri } = asset;
setImageUri(uri);
これは、キャンセルしたピッカーがアセットを読み取ることができないことと、codeがアセットを読み取ることを前提としていることの2つの失敗ケースを取り扱っています。 result.assets[0] 常に存在する場合、実行時には失敗します。
URIが得られたら、プレビューのレンダリングは簡単です:
<Image source={{ uri: imageUri }} style={{ width: 240, height: 240 }} />
アップロードする場合に後で使用する場合は、URIだけではなく、オブジェクト全体を保持しておくことをお勧めします。 asset 実際には、 fileName, mimeType, width, height、 fileSize は、検証、ログ、またはクリーンな多部品要求の作成に便利です。
選択画面以外の下流の動作を変更するオプション
いくつかのピッカーのオプションは、ファイルサイズ、編集の動作、バックエンドが受け入れる必要があるものなど、選択画面以外の動作を変更します。
| オプション | タイプ | 変更されること | 一般的な使用法 |
|---|---|---|---|
mediaTypes |
配列 | ユーザーが選択できるものを制限します | API が画像のみを受け入れる場合に限り、画像のみを選択できるようにします |
allowsEditing |
boolean | OS がサポートしている場合に、カットや編集の UI を提供します | アバター、正方形のカバー、受領のキャプチャ |
quality |
数値 | サポートしている画像出力を圧縮します | モバイルネットワークでのアップロードサイズを削減します |
base64 |
boolean | 結果にエンコードされた画像データを追加します | 明示的にインライン画像データが必要な統合のみに使用してください |
A few trade-offs are easy to miss:
allowsEditingは、画像スロットが固定形状またはサイズを持つ場合に便利です。サーバーが独自のクロップパイプラインを実行し、元のファイルを取得したい場合には、有用ではありません。qualityアップロード時間、メモリ圧力、サーバー ストレージに影響します。quality: 1自動的に正解ではありません。mediaTypesバックエンドのルールと一致するようにしてください。サーバーが動画を拒否する場合、ピッカーが動画を返さないようにしてください。base64メモリ内でのペイロードサイズを増加します。受信サービスがそれを必要としない場合は避けてください。
最後の点は、メモリが少ないデバイスでは重要です。ローカルファイルURIは、プレビューとマルチパートアップロードのためのよりよいハンドオフです。Base64は有効な使用例がありますが、ファイル参照を渡すことと比較して高価です。
URI versus base64
ほとんどのアプリでは、ルールは簡単です:
- 使用する URI プレビュー用
- 使用 URI ファイルアップロード用
- 使用 base64 受信システムがエンコードされたコンテンツを要求している場合にのみ使用してください。
That pattern keeps picker code small and easier to test. It also lines up with how many backend media flows are built, including services that eventually publish to external platforms such as the Instagram media publishing API.
チームが頻繁にOTA更新を実行したり、画像重いアセットをアプリ配信で移動したりする場合、ここでのファイルサイズの決定はパイプラインの残りの部分に影響を与えます。このガイド "アプリ更新用の画像の最適化" はピッカーの構成とともに役立ちます。 実用的なアプリ用の安全な結果パターン
__CAPGO_KEEP_0__
デモ用途ではcodeを保存するだけで十分です。 imageUri 実稼働環境では、次のステップであるプレビュー、検証、アップロード、またはリトライのために、元のピッカーのレスポンスを再解釈する必要がなくなるように、正規化されたオブジェクトを保存することをお勧めします。
const result = await ImagePicker.launchImageLibraryAsync({
mediaTypes: ['images'],
allowsEditing: true,
quality: 0.8,
});
if (result.canceled || !result.assets?.length) {
return;
}
const asset = result.assets[0];
setSelectedImage({
uri: asset.uri,
fileName: asset.fileName ?? 'upload.jpg',
mimeType: asset.mimeType ?? 'image/jpeg',
width: asset.width,
height: asset.height,
fileSize: asset.fileSize ?? null,
});
これにより、内部のアプリケーション内で一貫した形状が得られます。また、管理型と裸のプロジェクトを同期することが容易になります。なぜなら、アプリケーションcodeが安定している間、ネイティブの差異については別の場所で取り組むことができます。
最後のチェックも役立ちます。追加の結果フィールドを有効にするのではなく、必要なデータを要求し、ピッカーを選択に集中させ、一般的なファイル処理ステップに変えるのではなく、ピッカーを使用してください。
高度なパターンとプラットフォームの差異
ピッカー機能は、最初の選択された画像がリトライ、認証ヘッダー、ネイティブの許可差異、実際のアップロードエンドポイントなどを乗り越えることができるようになるまで、簡単ではありません。 expo-image-picker 選択をうまく処理することはできます。残りの機能はあなたのアプリケーションに任せます。

実践的なアップロードパターン
API がファイルアップロードを期待している場合、 FormData は依然として最も安全なデフォルトです。Rails、Node、Laravel、Django、Go の共通バックエンドで動作し、ピッカーをトランスポートの懸念から分離することができます。
async function uploadImage(imageUri: string) {
const formData = new FormData();
formData.append('file', {
uri: imageUri,
name: 'upload.jpg',
type: 'image/jpeg',
} as any);
const response = await fetch('https://your-api.example.com/uploads', {
method: 'POST',
body: formData,
headers: {
Accept: 'application/json',
},
});
if (!response.ok) {
throw new Error('Upload failed');
}
return response.json();
}
That code is enough to prove the path works, but production apps usually need one more layer. Derive name そして type 選択したアセットから可能な限り、認証をピッカー関数の外側に追加し、アップロード状態をピッカー状態と分離して、失敗した要求がユーザーにライブラリを開くことを強制しないようにします。
レビューでよく見る一般的なエラーを防ぐために、いくつかのチェックがあります。
- ローカル
uriリクエストを構築する前に存在することを確認する - アップロードする前にプレビューを表示する
- リクエストが飛行中の間、ユーザーが間違ったファイルを早期に検出できるようにする
- リクエストが飛行中の間、ユーザーがリクエストをキャンセルしたりパーミッションエラーを発生させたりしないようにする
- ネットワークエラーをピッカーのキャンセルまたはパーミッションエラーと別々に処理する
バックエンド検証が大きいファイル、非対応のMIMEタイプ、または認証が欠けている場合に拒否することを期待する
バックエンドがbase64代わりにmultipartを要求する場合、それは通常サーバー制約であり、ピッカーの要件ではありません。Multipartはメモリのコストが安く、モバイルで推論が容易なので、より安心して使用できます。
The picker UI is native, so it inherits native behavior. That affects both what users see and what your code should assume.
On iOS, editing flows and permission prompts follow Apple’s conventions. Limited Photos access can return a narrower set of assets than your test account saw on a fully granted device. On Android, picker behavior varies more by OS version and manufacturer skin, especially around albums, file names, and how camera captures are returned. Bare React Native apps feel these differences more directly because you own more of the native setup, but managed Expo apps still need code that treats the picker as platform-shaped rather than perfectly uniform.
実用的なルールは簡単です。検証できるフィールドに依存してください。デバイス間で同じUIや同じメタデータに依存するのではなく。
実用的なルールは簡単です。検証できるフィールドに依存してください。デバイス間で同じUIや同じメタデータに依存するのではなく。
- 実際のアプリでは、以下の例が重要です: 編集とトリミング:
- iOSとAndroidのUIとトリミングの動作は同じではありません。
fileName,mimeType返されたメタデータ:fileSize、 - 存在しないか、不一致になる可能性があるため、代替値を追加してください。 許可:
- iOSの写真アクセスは選択されたアイテムに制限される場合があります。Androidの動作はOSバージョンとシステムピッカーのサポートに依存します。 カメラ出力:
チームがExpo以外の場所でも作業している場合、この デザインスタックアプリ開発ガイド Androidのメディアハンドリングの決定に現れるものを示す
管理されたワークフローと裸のワークフローの違い
この時点で、セットアップの選択肢は実行上の意味を持ち始めます。
管理されたワークフローでは、許可文字列とプラグインの設定は通常アプリの設定ファイルにあり、ネイティブの変更はビルドを作成するたびに適用されます。これにより、JavaScriptの表面面積がきれいになりますが、設定の修正は次のネイティブのビルドまで表示されません。OTAの更新は欠落しているネイティブの許可を修正しません。
裸のワークフローでは、同じ機能にはより多くの要素が含まれます。iOSのネイティブの使用説明、Androidのマニフェストの動作、パッケージのインストール、リビルドのタイミングを自分で確認する必要があります。利点はコントロールです。コストは、JavaScriptの呼び出しサイトではなくネイティブの構成によって問題が生じる可能性があることです。
Teams that switch between Expo and Capacitor often underestimate how different these abstraction layers are. Capgo has a useful explanation of 両方のワークフローで私の好みは一貫しています。ピッカーをCapacitorに狭くし、結果を一度に正規化し、専用の__CAPGO_KEEP_1__層を通じてアップロードし、プラットフォーム固有の動作を仮定せずに構成してテストするのではなく、明示的に設定してテストするのを優先します。トラブルシューティングの一般的な問題
My preference is consistent across both workflows. Keep picker code narrow, normalize the result once, upload through a dedicated API layer, and treat platform-specific behavior as something to configure and test explicitly rather than smooth over with assumptions.
__CAPGO_KEEP_1__
Expo イメージ ピッカーの一般的な問題のトラブルシューティング チェックリスト

Expo イメージ ピッカーの一般的な問題の迅速なチェック
Expo イメージ ピッカーが開かない場合やパーミッションが失敗した場合、まずネイティブの設定を確認してください。バーレス アプリでは、特に iOS の使用説明文が欠けていることが一般的な原因です。
ユーザーがピッカーを閉じた後、エラーが発生した場合、結果の処理を確認してください。多くの実装では、直接 URI を読み込むことを前提としているため、チェックを省略しています。 canceled 以下の簡単なマッピングを確認してください:
パーミッションが拒否されたエラー:
- アプリの設定とネイティブのパーミッション文字列を確認し、再構築してください。 image URI:
undefinedから ではなくresult.assets?.[0]?.uri, notresult.uri.- キャンセルを押しても何も起こらない: それが正しいかもしれません。キャンセルを無視してください。
- 画像が表示されない: URIが保存されたステートと渡されたことを確認してください。
<Image source={{ uri }} />. - シミュレータでカメラが奇妙に動作する: 物理デバイスでテストしてください。ライブラリのバグを追いかける前に。
短いリリースチェックリスト
本番用にリリースする前に最後のチェックとして使用してください:
- Expoツールを使用してインストール: 使用:
npx expo install expo-image-picker. - ネイティブの部分を設定: プラグインと必要なパーミッションの説明を追加してください。
- 許可を意図的に要求する: カメラとメディアライブラリのフローを分離する。
- 結果をすべて保護する: 確認
result.canceled安全に読み取るassets[0]. - URIベースのアップロードを優先する: base64を特別なケースのみに保つ。
- 実機でテストする: 特にカメラキャプチャと許可のポップアップの場合。
If your team ships Capacitor or Electron apps alongside React Native projects, Capgo CapgoのUIから長い文字列のキー`submitting_a_pr_to_capgo`の親キー。ページ/エリア: Capgoのマーケティングウェブサイト。役割: ウェブサイトのコピー。見られる場所: contributing.astroページ。Capgoの製品/ブランド名と開発者用語を完全に保持する。メッセージキー`submitting_a_pr_to_capgo`(Capgoにプルリクエストを提出する)。