HEIC to JPG for Developers: APIs, Libraries and Browser
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.