Back to Blog
·4 min read

How to batch convert HEIC to JPG on Mac (no upload)

How to batch convert HEIC to JPG on Mac (no upload)

The "no upload" part isn't paranoia — it's the whole point

Most "HEIC to JPG converter" results are web uploaders. You drag your photos into a browser, they go to someone's server, you download a zip. For a couple of vacation shots, fine. For a folder of 400 iPhone photos — some of which you'd rather not hand to a random SaaS — that's the wrong tool.

Here's the thing people miss: your Mac already converts HEIC to JPG natively, in bulk, with zero uploads. HEIC decoding and JPEG encoding both ship in macOS via Apple's ImageIO framework. Every method below runs 100% on-device. Pick one by how much you care about control.

Option 1: Preview — the zero-install path

If you just need it done, Preview handles batches without a single download.

1. Select all your .heic files in Finder, open them together in Preview (they'll stack in the sidebar). 2. In the sidebar, select all thumbnails (Cmd+A). 3. File ▸ Export Selected Images… 4. Click Options, choose JPEG as the format, set quality, and export to a folder.

Preview's export is backed by the same system image stack, so the JPEGs come out color-correct and EXIF-aware. The catch: quality is a slider, not a number — you can't pin an exact compression level, and there's no scripting it. It's the low-effort option, and for most people it's enough.

> Apple's docs cover the single-image flow under Convert image types in Preview — the batch trick is just selecting all sidebar thumbnails before exporting.

Option 2: sips — one command, the whole folder

When you want this repeatable and exact, drop to the command line. sips (Scriptable Image Processing System) is a built-in macOS tool that wraps ImageIO. No install, no upload, works over SSH.

Convert every HEIC in the current folder to JPG:

``bash for f in .heic .HEIC; do [ -e "$f" ] || continue sips -s format jpeg "$f" --out "${f%.*}.jpg" done ``

Want to control quality and write into a separate jpg/ directory so you don't mix outputs with originals:

``bash mkdir -p jpg for f in .heic .HEIC; do [ -e "$f" ] || continue sips -s format jpeg -s formatOptions 80 "$f" --out "jpg/${f%.*}.jpg" done ``

formatOptions takes 0–100 (or low/normal/high/best). This is your CRF-equivalent for JPEG:

| formatOptions | Result | Use when | |---|---|---| | best / 95–100 | Near-lossless, large files | Archiving, print | | 80 | Visually clean, ~half the size | Default for sharing | | normal / 60 | Noticeably smaller, slight artifacts | Web thumbnails, email | | low / 40 | Visible blocking | Only when size is everything |

Recurse into subfolders without flattening your structure:

``bash find . -type f \( -iname '.heic' \) -print0 | while IFS= read -r -d '' f; do sips -s format jpeg -s formatOptions 80 "$f" --out "${f%.}.jpg" done ``

Option 3: ImageIO directly — when you need control sips won't give

sips is convenient but blunt. If you need to strip or rewrite metadata, force a color profile, downscale during conversion, or process thousands of files with predictable memory behavior, go to the framework sips itself sits on: ImageIO.

ImageIO gives you CGImageSource (to read HEIC) and CGImageDestination (to write JPEG), with explicit control over compression quality, orientation, and which EXIF/GPS keys survive the round-trip. A minimal Swift sketch:

```swift import ImageIO import UniformTypeIdentifiers

func heicToJPG(at src: URL, to dst: URL, quality: CGFloat = 0.8) { guard let source = CGImageSourceCreateWithURL(src as CFURL, nil), let image = CGImageSourceCreateImageAtIndex(source, 0, nil), let dest = CGImageDestinationCreateWithURL( dst as CFURL, UTType.jpeg.identifier as CFString, 1, nil) else { return }

CGImageDestinationAddImage(dest, image, [ kCGImageDestinationLossyCompressionQuality: quality ] as CFDictionary) CGImageDestinationFinalize(dest) } ```

Wrap that in a loop over a directory and you have a converter you fully own — including the decision of whether location data leaves the original or not. This is the most-control tier: more code, but every byte of behavior is yours, and nothing ever touches a network.

Which one should you actually use?

  • A handful of photos, one time → Preview. Don't overthink it.
  • You do this regularly → the sips loop, saved as a shell function or a Finder Quick Action.
  • You need exact metadata/quality/scaling control, or you're building it into an app → ImageIO.

All three share the only feature that matters here: the files never leave your Mac. The "no upload" web converters aren't just slower for batches — they're solving a problem your machine already solved years ago.

Sources

#batch#convert#heic#jpg

Written by

Peter Zhang

Building local-first Mac & iOS productivity apps at Obelisk Club.