npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

react-native-simple-heic2jpg

v0.4.0

Published

React Native Component for converts HEIC files on Android and iOS

Readme

react-native-simple-heic2jpg

React Native native module for converting local HEIC/HEIF images to JPEG on Android and iOS.

Installation

npm install react-native-simple-heic2jpg

# or
yarn add react-native-simple-heic2jpg

# For iOS
cd ios && pod install

Features

  • Image format conversion

    • Converts local HEIC/HEIF images to JPEG.
    • JPEG and PNG inputs are passed through without conversion.
  • EXIF metadata preservation

    • Copies supported metadata such as GPS, camera, orientation, and date fields from the source image to the converted JPEG.
    • Exact metadata preservation depends on platform image/EXIF support.
  • iOS and Android support

    • iOS implementation: Objective-C++ using ImageIO/CoreImage.
    • Android implementation: Kotlin using BitmapFactory and AndroidX ExifInterface.
    • Supports React Native old architecture and TurboModule/new architecture wiring.
  • Simple JavaScript API

    • One exported helper: convertImage(imagePath, options?).

Usage

import { convertImage } from 'react-native-simple-heic2jpg';

const result = await convertImage(path);

To receive raw base64 instead of a file URI:

const base64 = await convertImage(path, { returnBase64: true });

JPEG quality

Pass quality to control the JPEG encode quality of converted HEIC/HEIF images:

// Smaller file, default quality
await convertImage(path);

// Higher quality, larger file
await convertImage(path, { quality: 95 });
  • quality is an integer from 0 (smallest) to 100 (best). It defaults to 80.
  • Out-of-range values are clamped to 0100; fractional values are rounded.
  • Quality only applies to HEIC/HEIF inputs that are converted. JPEG and PNG inputs pass through without re-encoding, so quality has no effect on them.

Note: this release standardizes the JPEG quality across platforms. Previously Android encoded at 100 and iOS used the system default; both now default to 80, so converted JPEGs are noticeably smaller. Pass quality: 100 for the maximum-quality (largest) output.

Input path contract

convertImage accepts local image files as either:

  • raw local file paths, for example /var/mobile/.../IMG_0001.HEIC or /storage/emulated/0/.../IMG_0001.HEIC
  • local file:// URIs, for example file:///var/mobile/.../IMG_0001.HEIC

content:// URIs are not supported by this release. Resolve them to a local file path before calling convertImage.

Stripping metadata

Pass stripExif or stripGps to drop metadata from the converted JPEG:

// Remove GPS only (keeps camera info, dates, orientation)
await convertImage(path, { stripGps: true });

// Remove all EXIF/GPS except the orientation tag
await convertImage(path, { stripExif: true });
  • stripGps: true removes only GPS tags.
  • stripExif: true removes all EXIF and GPS metadata except the orientation tag (which is kept so the image still renders upright). stripExif implies stripGps.
  • Both default to false.

Stripping only applies to HEIC/HEIF inputs that are converted. JPEG and PNG inputs are passed through without re-encoding, so their metadata is returned untouched — convertImage(jpgPath, { stripGps: true }) does not modify the original JPEG.

Injecting GPS coordinates

Pass gps to write coordinates into the converted JPEG:

await convertImage(path, {
  gps: { latitude: 37.5665, longitude: 126.978 },
});
  • The injected coordinates replace whatever GPS the source carried.
  • Injection wins over stripGps / stripExif for the GPS block only: strip removes the source GPS first, then the provided coordinates are written. Other EXIF is unaffected.
  • Signed decimal degrees; the hemisphere refs (N/S, E/W) are derived from the sign.
  • HEIC/HEIF input: coordinates are written onto the converted JPEG.
  • JPEG input: normally a pass-through, but when gps is provided the JPEG is copied (metadata rewritten, pixels not re-encoded) and the returned path points to the injected copy — the original file is never mutated. Camera captures, which arrive as JPEG, get GPS this way.
  • PNG input: always a pass-through; PNG has no reliable EXIF container, so gps is ignored.

This pairs with the photo-picker section below: when Android's system picker hands you a file with zeroed GPS, your app can re-attach coordinates it knows from another source (its own location fix, MediaStore query, or user input).

Platform note: on iOS, stripExif removes the EXIF and GPS metadata blocks but may retain camera make/model fields stored in the TIFF block. GPS is removed on both platforms; Android's stripExif also drops make/model. Full parity is deferred to a future major release.

GPS metadata and the Android photo picker

If the converted output has no GPS tags even though the original photo has them, check how the input file reached your app before suspecting this library. On Android, files returned by the system photo picker (PICK_IMAGES / PickVisualMedia) have their GPS EXIF values zeroed out by the OS before your app ever sees the bytes. This is Android's scoped-storage location redaction (the system hides location information by default), and it cannot be bypassed for picker URIs — MediaStore.setRequireOriginal() is rejected for them even with the ACCESS_MEDIA_LOCATION permission (AOSP PickerUriResolver: "Require Original is not supported for Picker URI").

This library preserves GPS whenever the input file actually contains it. To feed it GPS-bearing inputs on Android, use a path the OS does not redact:

  • files your app already owns on disk (app storage, downloads you wrote, camera output)
  • the Storage Access Framework / documents picker (ACTION_OPEN_DOCUMENT)
  • a MediaStore URI resolved with ACCESS_MEDIA_LOCATION + MediaStore.setRequireOriginal(), copied to a local file first (content:// is not accepted directly — see the input path contract)

EXIF orientation policy

Both platforms preserve the EXIF orientation tag but do not rotate the pixels:

  • The converted JPEG carries the source's Orientation tag unchanged.
  • The pixel data is written unrotated (Android decodes raw pixels with BitmapFactory; iOS keeps the CIImage unrotated).
  • Your app — or the image component you render with — is responsible for interpreting the orientation tag, exactly as it would for the original HEIC.

This behavior is identical on iOS and Android.

Return value

By default, the JavaScript API resolves to a file:// URI string.

  • HEIC/HEIF input: returns the converted JPEG file URI.
  • JPEG input: returns the original file URI — unless gps is provided, in which case it returns a new injected-copy URI (the original is left untouched).
  • PNG input: returns the original file URI (gps is ignored).

When returnBase64 is true, the JavaScript API resolves to a raw base64 string.

  • HEIC/HEIF input: returns the converted JPEG bytes as base64.
  • JPEG/JPG input: returns the original file bytes as base64 — unless gps is provided, in which case it returns the injected copy's bytes.
  • PNG input: returns the original file bytes as base64 (gps is ignored).
  • The returned string does not include a file:// prefix.
  • The returned string does not include a data:image/...;base64, prefix. Add one in your app if your target component requires a data URI.
  • HEIC/HEIF base64 mode may use temporary/cache files internally so the returned base64 comes from finalized JPEG bytes with preserved metadata. Generated temporary/cache files are cleaned after encoding.
  • Base64 increases memory usage compared with URI mode, so URI mode remains the default and is recommended for large images.

Error handling

The promise rejects on failure. New failure paths carry a stable error.code you can branch on:

| error.code | Platform | When | | --- | --- | --- | | E_HEIF_UNSUPPORTED_OS | Android | HEIC/HEIF decode attempted below Android 9 (API 28), where the platform has no HEIF decoder. | | E_HEIF_DECODE_FAILED | Android | The input could not be decoded (corrupt or not a real HEIC/HEIF). | | E_UNSUPPORTED_URI | Android | A content:// URI was passed. Resolve it to a local file path first. |

Platform differences

  • content:// rejection happens on both platforms, but the code differs: Android rejects with E_UNSUPPORTED_URI, while iOS rejects with its existing Unsupported URI code. The codes are intentionally left unaligned in this release to avoid changing the established iOS code (a breaking change); full alignment is deferred to a future major.
  • Pass-through inputs (JPEG/PNG) are never re-encoded, so stripExif / stripGps have no effect on them on either platform.
  • Existing iOS reject codes (for example Unsupported Image Format) are unchanged in this release.

License

MIT