CLI tool
react-native-cameraroll/react-native-cameraroll avatar
react-native-cameraroll/react-native-cameraroll

CameraRoll is the React Native module for the device photo library

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

1,079 stars445 forksJavaMIT

At a glance

What is it?
@react-native-camera-roll/camera-roll gives a React Native app access to the local camera roll, with a native Java implementation on Android and an Objective-C module on iOS. The readme spends most of its length on the two things that actually break in production, which are the permission prompts and the difference between the current Android media permissions and the old storage ones.
Who is it for?
Adopt CameraRoll when a React Native app has to read or write the device photo library, and you are prepared to implement the permission flow on both platforms rather than assume the module does it. Do not adopt it on an assumption that the permission is granted, because on iOS the app exits if it saves without the add usage description, and on Android the media permissions have to be requested explicitly.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 8 days ago.
What is it written in?
Mainly Java, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.

Editorial analysis

A native module for the one thing apps cannot avoid

CameraRoll is a React Native native module that provides access to the local camera roll or photo library, and the readme says the same in the usage section.

It is one of those modules that exists because the platform has the capability and React Native does not expose it. Every app that lets a user pick a photo hits the same wall, and the alternative to writing the native module yourself is to write the native module yourself.

The implementation is Java on the Android side, and the package manifest shows the shape of a modern React Native library: the source under src/ with a TypeScript entry point, the compiled CommonJS, ES module and type declaration outputs under lib/, the android/ and ios/ native directories, a podspec for the iOS build, and the README.

The licence is MIT, the author is Bartol Karuza, and the last push was on 2026-10-15. The recent releases are v7.10.2 from 2025-08-06, v7.10.1 from 2025-06-25 and v7.10.0 from 2025-03-03, so the release line is moving at a steady, unhurried pace.

The badges show Circle CI for the build, a supported operating systems badge for Android and iOS, and a Lean Core badge, which is the React Native community programme for the libraries the ecosystem depends on.

The package name changed, and the readme says so first

The very first thing in the readme, before anything else, is a notice.

The NPM package name has changed, so change your package.json dependency. The previous package name was @react-native-community/cameraroll and the new one is @react-native-camera-roll/camera-roll.

That is a real migration, not a cosmetic one, and the reason is visible in the layout. The new scope is hyphenated where the old one was not, and the Android module name in the manual linking instructions follows it, with the project name include ':@react-native-camera-roll_camera-roll' and the package class still com.reactnativecommunity.cameraroll.CameraRollPackage. So the Java package name did not change, the npm scope did, and the Gradle project name is derived from the npm one.

The consequence for an existing project is that the import path changes too, and there is a section for it. The module was created when the CameraRoll was split out from the core of React Native, so to migrate you install the package and then change your imports from react-native to the new package.

javascript
import { CameraRoll } from "@react-native-camera-roll/camera-roll";

The old import is still in the readme as the before case, which is the clearest way to see what has to change.

Installation, auto linking and what to do when it fails

The install is one command.

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

Or with yarn.

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

Linking should be automatic since React Native version 0.60, and the readme gives the manual route for the cases where it is not.

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

The manual instructions exist for older projects and for the cases where the autolinking does not pick the module up. On iOS that means adding the RNCCameraroll.xcodeproj from node_modules to the Libraries group in Xcode, and then adding libRNCCameraroll.a to the Link Binary With Libraries build phase.

On Android it means three edits. The MainApplication.java needs the import of com.reactnativecommunity.cameraroll.CameraRollPackage and a new CameraRollPackage() in the list returned by getPackages(), though the note says auto linking means this is not required from React Native 0.69. Then android/settings.gradle needs the include and project lines, and android/app/build.gradle needs the implementation line in its dependencies block.

The note about the version matters, because a project on an old React Native will be doing all three by hand, and the instructions are written so you can tell which steps still apply.

iOS: two usage descriptions, and an exit if you get it wrong

The iOS permissions section is the clearest statement of what a native module can and cannot do for you, and it is worth reading as a warning.

The user's permission is required to access the Camera Roll on devices running iOS 10 or later, and you add the NSPhotoLibraryUsageDescription key in your Info.plist with a string that describes how your app will use this data. The key appears in Xcode as Privacy - Photo Library Usage Description.

If you are targeting devices running iOS 11 or later, you also need the NSPhotoLibraryAddUsageDescription key, and by adding it you are able to request write-only access permission from the user. Then the sentence that matters: if you try to save to the camera roll without this permission, your app will exit.

That is the behaviour of the operating system rather than of this library, and it is a nasty one to debug because it presents as a crash on a specific action rather than as a missing permission. An app that reads photos but never writes them needs only the first key, and an app that saves needs both, and the difference is one line in the plist.

The strings themselves are reviewed by Apple, which is the other reason to write them properly rather than to paste a placeholder. The key is shown in Xcode as a description of your app's use of the data, so a reviewer reading your app sees that text.

Android: the media permission split at API 33

On Android, permission is required to read and write to the external storage, and the manifest entries are the first step.

xml
<manifest>
  <uses-permission android:name="android.permission.READ_MEDIA_IMAGES" />
  <uses-permission android:name="android.permission.READ_MEDIA_VIDEO" />
  <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"
    android:maxSdkVersion="32" />
  <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
</manifest>

The maxSdkVersion of 32 on the read external storage line is the mechanism, and it is worth understanding. The granular media permissions arrived in Android 13, and the broad storage permission is kept for the older releases, so declaring both with the cap keeps one manifest working across the range.

Then the permission has to be requested explicitly, and the readme's example is a good template for it. The check function branches on Platform.Version: at 33 and above it checks READ_MEDIA_IMAGES and READ_MEDIA_VIDEO with a Promise.all and requires both, and below 33 it checks the single READ_EXTERNAL_STORAGE permission instead. The request function does the same split, using requestMultiple for the two new permissions and request for the old one, and the example's save function returns early on Android if the permission is not held.

For Expo there is a separate guide for requesting the permission, and for react-native-cli or ejected apps the readme points at the Android permissions documentation, because the manifest entry only declares the capability, it does not ask for it.

The method surface, and how save decides photo from video

The methods section lists seven entries, and one of them is marked as iOS only.

save, getAlbums, getPhotos, deletePhotos, iosGetImageDataById, useCameraRoll and getPhotoThumbnail, the last of which is iOS only. The getPhotoThumbnail name in particular is a good example of a method that could not be abstracted across the platforms, because the underlying gallery APIs differ.

The save method is the one with behaviour worth knowing.

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

It saves the photo or video to the photo library and returns the URI of the newly created asset. The tag must be a local image or video URI, such as a file URI in the sdcard. If the tag has a file extension of .mov or .mp4, in either case, it is inferred as a video, and otherwise it is treated as a photo, and you can override the automatic choice with the optional type parameter, which must be either photo or video.

The album parameter names the album to store the asset in. On Android, if no album is provided the DCIM directory is used, and otherwise the PICTURE or MOVIES directory is used depending on the type provided.

So the inference is by extension, which is a heuristic, and the override exists because heuristics fail. A file with no extension, or one with an unusual one, will be classified as a photo unless you pass the type.

The toolchain runs three linters and a device test suite

The package manifest is more interesting than a native module's usually is, because the validation chain is long.

The test script runs yarn validate:eslint, then yarn validate:flow, then yarn validate:typescript, and then yarn test:jest. So the code is checked by ESLint, by Flow and by the TypeScript compiler before the unit tests run, which is a belt and braces approach that catches a lot in a library with a large native surface.

The build is bob build, run from the prepare script, and the outputs are the three directories the files list mentions, lib/commonjs, lib/module and lib/typescript, with the main, module and types fields pointing at them and a react-native field pointing at src/index.ts so that a consumer in a development build uses the source.

Below that there is a Detox suite, with separate scripts for the debug and release configurations on both Android and iOS, a build script for each, and a bundle script that produces an Android bundle with react-native bundle using four workers. So the library has real end to end device tests, in both debug and release variants, which is the part that catches the difference between a module that works in a simulator and one that works in a shipped build.

The repository layout supports that, with an example/ directory that is a full application with its own Gemfile, metro and jest configuration and a ruby version file, plus a FabricExample/ directory for the new architecture, an ios/ and android/ pair, and the usual linting configuration.

Editorial conclusion

Adopt CameraRoll when a React Native app has to read or write the device photo library, and you are prepared to implement the permission flow on both platforms rather than assume the module does it. Do not adopt it on an assumption that the permission is granted, because on iOS the app exits if it saves without the add usage description, and on Android the media permissions have to be requested explicitly. Verify first that you have renamed the dependency, since the package moved from the community scope, and that your Info.plist carries both usage description keys and your manifest carries the permissions for the API level you target.

Frequently asked questions

What is @react-native-camera-roll/camera-roll?

It is a React Native native module that provides access to the local camera roll or photo library, with a native Java implementation for Android and a module for iOS, licensed under MIT.

The package name changed, what do I need to do?

The previous name was @react-native-community/cameraroll and the new one is @react-native-camera-roll/camera-roll. Change the dependency in package.json and change your imports from react-native to @react-native-camera-roll/camera-roll, since the module was split out of the React Native core.

What permissions does CameraRoll need on iOS?

The NSPhotoLibraryUsageDescription key in your Info.plist, and the NSPhotoLibraryAddUsageDescription key as well if you target iOS 11 or later and want to save. The readme warns that if you try to save to the camera roll without that permission, your app will exit.

Which Android permissions does CameraRoll need?

READ_MEDIA_IMAGES and READ_MEDIA_VIDEO for the granular media permissions, READ_EXTERNAL_STORAGE with android:maxSdkVersion="32" for older releases, and WRITE_EXTERNAL_STORAGE for writing. The permission then has to be requested explicitly, with the request call branching on whether Platform.Version is 33 or above.

How does CameraRoll.save decide between a photo and a video?

By file extension. A tag ending in .mov or .mp4 is inferred as a video and anything else is treated as a photo, and you can override the inference with the optional type parameter, which must be photo or video. It returns a promise resolving with the URI of the new asset.

Does CameraRoll need manual linking?

Not usually. Linking should be automatic since React Native 0.60, and the readme gives the manual steps for iOS and Android for the cases where it is not, including the MainApplication.java edit, which is not required from React Native 0.69 with auto linking.

Official sources

  1. Issues
  2. License: MIT
  3. react-native-cameraroll/react-native-cameraroll on GitHub
  4. README
  5. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/react-native-cameraroll-react-native-cameraroll.svg)](https://hysenlabs.com/projects/react-native-cameraroll-react-native-cameraroll)

Was this useful?