DEV Community

Nawal Shrestha
Nawal Shrestha

Posted on Originally published at getsnapshrink.com

I Added Client-Side HEIC to JPG Conversion to My Image Tool — No Server, No Upload

Most "free HEIC to JPG" tools work the same way: you upload your iPhone photos to someone's server, it converts them, you download the result. For a format conversion — one of the most local operations imaginable — that's a strange trade.

I wanted HEIC to JPG support in SnapShrink (my in-browser batch image compressor) without breaking its core promise: nothing ever uploads. Here's how I did it.

The decoder: heic2any, lazy-loaded

Browsers can't decode HEIC natively (unlike AVIF, which Chrome/Edge/Firefox handle via createImageBitmap). So the conversion needs a real decoder. I used heic2any, pinned to a fixed version, loaded via dynamic import() only when a HEIC file is actually dropped.

That last part matters: the decoder is a chunk of WASM/JS you don't want in the initial bundle for the 95% of users who never touch HEIC. Lazy-loading keeps the first paint fast for everyone.

The pipeline

Once decoded, a HEIC file is just pixels — it flows into the exact same pipeline as every other format:

  1. Decode → ImageData/canvas
  2. Quality slider or Target KB mode (binary-search the quality setting until the output fits)
  3. Encode to JPG/PNG/WebP, batch, ZIP download

No HEIC-specific special cases downstream. That was the whole point of the architecture: new input formats are a decode step, not a new app. AVIF support later followed the same path — same idea, native decode.

The UX details that matter

  • Files get a small "HEIC" badge in the drop list so users can see what was detected.
  • A misnamed file (.heic that's actually a JPEG) gets a clear error instead of a cryptic decoder failure.
  • Target KB mode works on HEIC too — type 100, get ≤100KB. This is the feature people actually write in about: forms with hard size caps. (How it works.)

Try it

Drop a real iPhone photo on getsnapshrink.com/heic-to-jpg/ and watch DevTools → Network while it converts. Zero file uploads — that's the entire architecture, and HEIC didn't change it.

First 10 images per session free, unlimited is a one-time $9. Happy to answer questions about the implementation in the comments.

Top comments (1)

Collapse
 
omyvnss profile image
Om Yaduvanshi •

nice. one builder's question on the misnamed-file error: does it sniff magic bytes or just the extension? the files people misname are exactly the ones an extension check misses.