HEIC to JPG for Developers: APIs, Libraries and Browser

Published

If your app accepts photos from iPhones, you will receive HEIC files, and most browsers cannot display them. There are three ways to turn them into JPG: send the file to a hosted API, run a decoding library on your own server, or decode it in the user’s browser and upload only the JPG. The main difference is where the original photo goes. This guide compares the three using each project’s own documentation, checked on 31 October 2026.

The three approaches at a glance

Approach Where the HEIC is decoded Examples What to check
Hosted API On the provider’s servers CloudConvert API, Cloudinary Files are uploaded to and stored by the provider
Self-hosted library On your server libheif, ImageMagick, libvips / sharp Your build must include an HEVC decoder
In the browser On the user’s device heic-to, heic2any, libheif-js Library size, user’s CPU and memory, metadata

Hosted APIs

CloudConvert API. Work is done in jobs made of tasks: import the file, convert it, export the result. CloudConvert’s public format list shows heic to jpg with the imagemagick engine at 1 credit. A minimal job, sent as POST https://api.cloudconvert.com/v2/jobs:

{
  "tasks": {
    "import-my-file": { "operation": "import/url", "url": "https://example.com/photo.heic" },
    "convert-my-file": { "operation": "convert", "input": "import-my-file", "input_format": "heic", "output_format": "jpg" },
    "export-my-file": { "operation": "export/url", "input": "convert-my-file" }
  }
}

An import/upload task lets users “directly upload input files to CloudConvert, without temporary storing them on your server”. The privacy policy says files are “transferred to and temporarily stored on CloudConvert’s servers”, and the API reference says “Jobs are deleted automatically 24 hours after they have ended”. You can pin processing to eu-central (Germany) or us-east (Virginia, USA).

Cloudinary. Cloudinary’s format table lists HEIF (.heif, .heic) as supported for upload and for transformations. You upload the original to your Cloudinary account, then request it in another format: the docs say to use f_jpg when you need a fixed format, and that Cloudinary “converts the image to the requested format for delivery”.

With either service, the original photo leaves your infrastructure and is stored by the provider. We do not list prices here: both providers price by plan or credits, and we did not verify their current rates.

Self-hosted libraries

libheif is the HEIF library used by practically every open-source app that handles HEIC. Its README says it uses libde265 for HEIC decoding by default and x265 for encoding, and that x265 is GPL. libheif itself is LGPL. The heif-dec tool converts every image stored in a HEIF file:

heif-dec photo.heic photo.jpg

Bindings exist for Python (pillow_heif, pyheif), Go, Rust, .NET and Node.js. The README also notes that 89 security advisories were investigated and fixed in 2026 alone, so keep the library updated if you decode files from untrusted users.

ImageMagick reads and writes HEIC, but its format list says HEIC “Requires libheif delegate library”. Check your build with magick -list format, then convert:

magick photo.heic -quality 90 photo.jpg

sharp (libvips) for Node.js. sharp’s prebuilt binaries support “JPEG, PNG, Ultra HDR, WebP, AVIF, TIFF, GIF and SVG (input)”. For HEIC that uses HEVC compression, which covers iPhone photos, the documentation for sharp’s heif() output option requires “a globally-installed libvips compiled with support for libheif, libde265 and x265”. We tried this with the prebuilt sharp 0.35.4 on a Mac and a 12-megapixel iPhone 7 photo: metadata() reported heif and hevc, but converting to JPEG failed with heif: Decoder plugin generated an error. A file that passes a metadata check can still fail to decode.

On a Mac only, the built-in sips command converts HEIC with no installs. On macOS 26 it converted the same photo to a 4032×3024 JPG and kept its Exif data:

sips -s format jpeg -s formatOptions 90 photo.heic --out photo.jpg

We did not run heif-dec or magick, because neither is installed on our test machine. The commands follow the official docs.

In the browser

Decoding in the browser means the server only ever receives a JPG. The cost is a large script, and the work runs on the user’s CPU and memory.

Library License (npm) Latest version Notes from its README
heic-to LGPL-3.0 1.6.7, 27 Oct 2026 Follows libheif releases; heic-to/next for Web Workers, heic-to/csp for strict CSP
heic2any MIT 0.0.4, 29 Mar 2023 Browser only; does not copy metadata
libheif-js LGPL-3.0 1.23.5, 4 Oct 2026 libheif compiled with Emscripten for Node.js and the browser; low-level decoder API

The heic-to README shows this call:

import { heicTo } from "heic-to"

const jpeg = await heicTo({ blob: file, type: "image/jpeg", quality: 0.9 })

We ran the same call with heic-to 1.5.2 in Chromium on the iPhone 7 photo. It returned a 4032×3024 JPEG of 1,494,239 bytes with no Exif data. If you need the capture date or orientation tags, you have to copy them yourself.

How heicly does it

heicly is a web page, not an API. It uses heic-to 1.5.2 inside a Web Worker: the photo is decoded to an ImageBitmap and drawn onto an OffscreenCanvas, which also corrects orientation and scales images larger than 16,777,216 pixels proportionally. The canvas is then encoded as JPG at quality 90 by default. The most recently converted photo’s canvas is cached, so changing the quality slider re-encodes without decoding again. The capture date and camera details are written back into the JPG, and location only if the user turns it on.

Which approach to choose

  • You already use a media service: a hosted API is the least work, if your privacy policy allows uploading users’ photos to that provider.
  • Files must stay on your servers: use libheif, ImageMagick or libvips with HEVC decoding, and keep them updated.
  • You want the original photo to stay on the user’s device: convert in the browser and upload the JPG. Test with large photos on low-end phones.

Try a browser-side conversion

Want to see browser-side conversion before you build it? Try the converter below. It runs on this page, and your photos are not uploaded.

Drop your HEIC or HEIF photos here

or

Your files never leave your device — everything happens right here in your browser. After one conversion, the converter also works offline.

Select several HEIC or HEIF photos at once. Very large photos may be scaled down to fit your browser's limits; on a phone, convert big batches in smaller groups.

    Note: refreshing or closing this page clears the current tasks and results.

    For background on the format itself, see HEIC vs JPG and the photo formats guide.

    Sources

    Checked on 31 October 2026: CloudConvert API introduction, jobs, import files, format list and privacy policy; Cloudinary supported formats, image optimization and upload API; the libheif README; ImageMagick formats and command-line options; sharp installation and output options; npm pages for heic-to, heic2any and libheif-js.

    ← All articles