Baike.dev
All toolsOnline toolsAI codingTrendingOpen sourceNewsSubmit
Log in
< Back to tools
R

react-native-cameraroll

> 编程语言
Open source

CameraRoll is a react-native native module that provides access to the local camera roll or photo library.

1.1K stars0 likes2 views
WebsiteGitHub

About

CameraRoll is a react-native native module that provides access to the local camera roll or photo library.

@react-native-camera-roll/camera-roll

CircleCI Status Supports Android and iOS MIT License Lean Core Badge

Notice: The NPM package name has changed, please change your package.json dependency!

Previous package name: @react-native-community/cameraroll

New package name: @react-native-camera-roll/camera-roll

Getting started

$ npm install @react-native-camera-roll/camera-roll --save

or

$ yarn add @react-native-camera-roll/camera-roll

Linking

Linking should be automatic since react-native version 0.60. Below are instructions if auto linking does not work.

Mostly automatic installation

$ react-native link @react-native-camera-roll/camera-roll && npx pod-install

Manual installation

iOS

  1. In XCode, in the project navigator, right click Libraries ➜ Add Files to [your project's name]
  2. Go to node_modules ➜ @react-native-camera-roll/camera-roll and add RNCCameraroll.xcodeproj
  3. In XCode, in the project navigator, select your project. Add libRNCCameraroll.a to your project's Build Phases ➜ Link Binary With Libraries
  4. Run your project (Cmd+R) ...

...


Then you have to explicitly ask for the permission

```javascript
import { PermissionsAndroid, Platform } from "react-native";
import { CameraRoll } from "@react-native-camera-roll/camera-roll";

async function hasAndroidPermission() {
  const getCheckPermissionPromise = () => {
    if (Platform.Version >= 33) {
      return Promise.all([
        PermissionsAndroid.check(PermissionsAndroid.PERMISSIONS.READ_MEDIA_IMAGES),
        PermissionsAndroid.check(PermissionsAndroid.PERMISSIONS.READ_MEDIA_VIDEO),
      ]).then(
        ([hasReadMediaImagesPermission, hasReadMediaVideoPermission]) =>
          hasReadMediaImagesPermission && hasReadMediaVideoPermission,
      );
    } else {
      return PermissionsAndroid.check(PermissionsAndroid.PERMISSIONS.READ_EXTERNAL_STORAGE);
    }
  };

  const hasPermission = await getCheckPermissionPromise();
  if (hasPermission) {
    return true;
  }
  const getRequestPermissionPromise = () => {
    if (Platform.Version >= 33) {
      return PermissionsAndroid.requestMultiple([
        PermissionsAndroid.PERMISSIONS.READ_MEDIA_IMAGES,
        PermissionsAndroid.PERMISSIONS.READ_MEDIA_VIDEO,
      ]).then(
        (statuses) =>
          statuses[PermissionsAndroid.PERMISSIONS.READ_MEDIA_IMAGES] ===
            PermissionsAndroid.RESULTS.GRANTED &&
          statuses[PermissionsAndroid.PERMISSIONS.READ_MEDIA_VIDEO] ===
            PermissionsAndroid.RESULTS.GRANTED,
      );
    } else {
      return PermissionsAndroid.request(PermissionsAndroid.PERMISSIONS.READ_EXTERNAL_STORAGE).then((status) => status === PermissionsAndroid.RESULTS.GRANTED);
    }
  };

  return await getRequestPermissionPromise();
}

async function savePicture() {
  if (Platform.OS === "android" && !(await hasAndroidPermission())) {
    return;
  }

  CameraRoll.save(tag, { type, album })
};

Methods

  • save
  • getAlbums
  • getPhotos
  • deletePhotos
  • iosGetImageDataById
  • useCameraRoll
  • getPhotoThumbnail iOS only

Reference

Methods

save()

javascript
CameraRoll.save(tag, { type, album })

…

javascript
CameraRoll.getAlbums(params);

Returns a Promise with a list of albums

Parameters:

  • assetType : {string} : Specifies filter on asset type. Valid values are:
    • All // default
    • Videos
    • Photos
  • albumType : {string} : (iOS only) Specifies filter on type of album. Valid values are:
    • All
    • Album // default
    • SmartAlbum

Returns:

Array of Album object

  • id: {string}
  • title: {string}
  • count: {number}
  • type: {string} (iOS only)
  • subtype: {string | undefined} : See AlbumSubType type for possible values. iOS only.

getPhotos()

javascript
CameraRoll.getPhotos(params);

…

javascript
_handleButtonPress = () => {
   CameraRoll.getPhotos({
       first: 20,
       assetType: 'Photos',
     })
     .then(r => {
       this.setState({ photos: r.edges });
     })
     .catch((err) => {
        //Error Loading Images
     });
   };
render() {
 return (
   
     
     
       {this.state.photos.map((p, i) => {
       return (
         
       );
     })}
     
   
 );
}

Loading images with listeners and refetchs:

javascript
import { CameraRoll, cameraRollEventEmitter } from '@react-native-camera-roll/camera-roll';

import { useCallback, useEffect, useState } from 'react';

import { AppState, EmitterSubscription } from 'react-native';

interface GalleryOptions {
  pageSize: number;
  mimeTypeFilter?: Array;
}

interface GalleryLogic {
  photos?: ImageDTO[];
  loadNextPagePictures: () => void;
  isLoading: boolean;
  isLoadingNextPage: boolean;
  isReloading: boolean;
  hasNextPage: boolean;
}

const supportedMimeTypesByTheBackEnd = [
  'image/jpeg',
  'image/png',
  'image/heif',
  'image/heic',
  'image/heif-sequence',
  'image/heic-sequence',
];

export const useGallery = ({
  pageSize = 30,
  mimeTypeFilter = supportedMimeTypesByTheBackEnd,
}: GalleryOptions): GalleryLogic => {
  const [isLoading, setIsLoading] = useState(false);
  const [isReloading, setIsReloading] = useState(false);
  const [isLoadingNextPage, setIsLoadingNextPage] = useState(false);
  const [hasNextPage, setHasNextPage] = useState(false);
  const [nextCursor, setNextCursor] = useState();
  const [photos, setPhotos] = useState();

  const loadNextPagePictures = useCallback(async () => {
    try {
      nextCursor ? setIsLoadingNextPage(true) : setIsLoading(true);
      const { edges, page_info } = await CameraRoll.getPhotos({
        first: pageSize,
        after: nextCursor,
        assetType: 'Photos',
        mimeTypes: mimeTypeFilter,
        ...(isAndroid && { include: ['fileSize', 'filename'] }),
      });
      const photos = convertCameraRollPicturesToImageDtoType(edges);
      setPhotos((prev) => [...(prev ?? []), ...photos]);

      setNextCursor(page_info.end_cursor);
      setHasNextPage(page_info.has_next_page);
    } catch (error) {
      console.error('useGallery getPhotos error:', error);
    } finally {
      setIsLoading(false);
      setIsLoadingNextPage(false);
    }
  }, [mimeTypeFilter, nextCursor, pageSize]);

  const getUnloadedPictures = useCallback(async () => {
    try {
      setIsReloading(true);
      const { edges, page_info } = await CameraRoll.getPhotos({
        first: !photos || photos.length  {
    if (!photos) {
      loadNextPagePictures();
    }
  }, [loadNextPagePictures, photos]);

  useEffect(() => {
    const subscription = AppState.addEventListener('change', async (nextAppState) => {
      if (nextAppState === 'active') {
        getUnloadedPictures();
      }
    });

    return () => {
      subscription.remove();
    };
  }, [getUnloadedPictures]);

  useEffect(() => {
    let subscription: EmitterSubscription;
    if (isAboveIOS14) {
      subscription = cameraRollEventEmitter.addListener('onLibrarySelectionChange', (_event) => {
        getUnloadedPictures();
      });
    }

    return () => {
      if (isAboveIOS14 && subscription) {
        subscription.remove();
      }
    };
  }, [getUnloadedPictures]);

  return {
    photos,
    loadNextPagePictures,
    isLoading,
    isLoadingNextPage,
    isReloading,
    hasNextPage,
  };
};

deletePhotos()

javascript
CameraRoll.deletePhotos([uri]);

…

javascript
CameraRoll.iosGetImageDataById(internalID, true);

…

javascript

try {
// uri 'PH://xxxx'
const fileData = await CameraRoll.iosGetImageDataById(uri);
if (!fileData?.node?.image?.filepath) return undefined;
const uploadPath = imageData.node.image.filepath; // output should be file://...
// fetch or ReactNativeBlobUtil.fetch to upload
}
catch (error) {}

Note:

Sometimes when calling iosGetImageDataById, the image/video can be downloaded from iCloud. To be able to receive the progress of this download, you need to add a listener to the onProgressUpdate event and use it to render on the UI.

The event generated will be an object containing the image id (id) and the progress of the download (progress). The id is a string with the internalID you used to call iosGetImageDataById. The progress is a double ranging from 0 to 1, where 0 represents the start of the download and 1 represents the completion of the download.

javascript

import { progressUpdateEventEmitter } from '@react-native-camera-roll/camera-roll';

useEffect(() => {
  const subscription = progressUpdateEventEmitter.addListener(
    'onProgressUpdate',
    event => {
      // Render the progress of the image / video being 
      // downloaded using event.id and event.progress
    },
  );

  return () => {
    subscription.remove();
  };
}, []);

useCameraRoll()

useCameraRoll is a utility hooks for the CameraRoll module.

javascript
import React, {useEffect} from 'react';
import {Button} from 'react-native';
import {useCameraRoll} from "@react-native-camera-roll/camera-roll";

function Example() {
  const [photos, getPhotos, save] = useCameraRoll();

  return <>
     getPhotos()}>Get Photos
    {
      photos.map((photo, index) => /* render photos */)
    }
  ;
};

…

bash
javascript
export default function Thumbnail(props) {
  const [base64Image, setBase64Image] = useState(null);

  useEffect(() => {
    const getThumbnail = async () => {
      const options = {
        allowNetworkAccess: true,
        targetSize: {
          height: 80,
          width: 80
        },
        quality: 1.0
      };

      const thumbnailResponse = await CameraRoll.getPhotoThumbnail(props.image.uri, options);

      setBase64Image(thumbnailResponse.thumbnailBase64);
    };

    getThumbnail();
  }, []);

  const extension = props.image.extension;
  let prefix;

  switch (extension) {
    case 'png':
      prefix = 'data:image/png;base64,';
      break;
    default:
      //all others can use jpeg
      prefix = 'data:image/jpeg;base64,';
      break;
  }

  return (
    
  );
}

Known issues

IOS

If you try to save media into specific album without asking for read and write permission then saving will not work, workaround is to not precice album name for IOS if you don't want to request full permission (Only ios >= 14).

Issues· 0 open

View all issuesOpen on GitHub

No open issues yet, or sync has not completed.

> Tags

Java

No comments yet. Be the first to share.

> Details

PublishedAug 1, 2026
UpdatedSep 17, 2026
Category编程语言
PricingOpen source

> Related tools

T
TypeScript
JavaScript 的超集,为前端与全栈提供静态类型
P
Python
通用编程语言,广泛用于 Web、数据与 AI
G
Go
Google 推出的简洁高效系统语言