HEIC on the Web: Browser Support and Best Practices

A developer's guide to handling HEIC files in web applications — browser support status, server-side conversion, client-side processing, and content negotiation strategies.

heicweb developmentbrowser supportjavascripttechnical

Web browsers do not broadly support HEIC. Only Safari decodes HEIC natively. Chrome, Firefox, Edge, and Opera cannot display HEIC images without JavaScript polyfills. This creates a clear engineering requirement: accept HEIC in upload flows, but never serve HEIC to browsers.

This guide covers the full developer workflow -- detecting HEIC uploads, converting server-side and client-side, choosing delivery formats, and implementing content negotiation. For background on the format itself, see What is HEIC?.

Browser Support for HEIC

HEIC browser support is limited to Safari. No other major browser has committed to native HEIC decoding. HEVC patent licensing costs are the primary barrier.

| Browser | HEIC Support | Notes | | --- | --- | --- | | Safari | Yes (macOS 11+, iOS 11+) | Native HEVC decoder in Apple hardware | | Chrome | No | No plans for native support | | Firefox | No | Blocked by HEVC licensing costs | | Edge | No | Relies on OS codec; inconsistent results | | Opera | No | Same Chromium limitation as Chrome | | Samsung Internet | No | No native HEIC decoding |

Safari's support comes from Apple's hardware HEVC decoders, present in every Apple device since 2017. Other browser vendors refuse to pay HEVC patent royalties. This situation is unlikely to change.

For comparison, WebP has 97%+ browser support and AVIF has 93%+ support. Both are royalty-free.

HEIC MIME Types and File Extensions

Correct MIME type handling is essential for detecting HEIC uploads. The IANA-registered types are:

| MIME Type | Description | | --- | --- | | image/heic | Single HEIC image (HEVC codec) | | image/heic-sequence | HEIC image sequence (animation/burst) | | image/heif | Single HEIF image (generic, any codec) | | image/heif-sequence | HEIF image sequence |

Common file extensions are .heic and .heif. Apple's Live Photos use .heic for the still frame. Some cameras produce .heics for sequences.

Browsers may not populate the MIME type correctly. On some platforms, File.type returns an empty string for HEIC uploads. Always validate by file extension as a fallback.

Detecting HEIC Uploads

Reliable HEIC detection requires checking both the MIME type and the file extension. File signature (magic bytes) inspection adds a third layer of certainty.

function isHeicFile(file) {
  // Check MIME type
  const heicMimeTypes = [
    'image/heic',
    'image/heif',
    'image/heic-sequence',
    'image/heif-sequence',
  ];
  if (heicMimeTypes.includes(file.type.toLowerCase())) {
    return true;
  }

  // Fallback: check file extension
  const extension = file.name.split('.').pop()?.toLowerCase();
  return ['heic', 'heif', 'heics'].includes(extension);
}

For file input elements, set the accept attribute to include HEIC types:

<input
  type="file"
  accept="image/heic,image/heif,.heic,.heif,image/jpeg,image/png,image/webp"
/>

Including both MIME types and extensions in the accept attribute ensures compatibility across browsers and operating systems.

Server-Side Conversion

Server-side conversion is the most reliable approach for web applications that process user uploads. Three mature options exist.

Sharp (Node.js)

Sharp is the fastest Node.js image processing library. It uses libvips under the hood, which links to libheif for HEIC decoding.

import sharp from 'sharp';

async function convertHeicToWebP(inputBuffer) {
  return sharp(inputBuffer)
    .webp({ quality: 80 })
    .toBuffer();
}

async function convertHeicToJpg(inputBuffer) {
  return sharp(inputBuffer)
    .jpeg({ quality: 85, mozjpeg: true })
    .toBuffer();
}

Sharp handles HEIC input automatically when libheif is available. Install Sharp v0.33+ for full HEIC support. Processing a 12 MP HEIC image takes approximately 200-500ms on modern server hardware.

ImageMagick

ImageMagick supports HEIC through its libheif delegate. It is available on most Linux distributions and works well in Docker containers.

# Convert single file
magick input.heic -quality 85 output.webp

# Batch convert with resize
magick mogrify -format webp -quality 80 -resize 2048x2048\> *.heic

ImageMagick is slower than Sharp for programmatic use but excels at batch processing and CLI workflows.

libheif (C/C++)

libheif is the reference HEIF/HEIC decoder. Both Sharp and ImageMagick use it internally. Direct libheif integration gives maximum control over decoding parameters. Use libheif directly when building custom image pipelines in C, C++, Go, or Rust.

Client-Side Conversion

Client-side HEIC conversion eliminates server processing costs and avoids uploading potentially sensitive photos. Two JavaScript libraries handle HEIC decoding in the browser.

libheif-js

libheif-js compiles the C-based libheif library to WebAssembly. It provides low-level access to HEIC decoding, including multi-image containers and metadata extraction.

import libheif from 'libheif-js';

async function decodeHeic(buffer) {
  const decoder = new libheif.HeifDecoder();
  const images = decoder.decode(new Uint8Array(buffer));

  const image = images[0];
  const width = image.get_width();
  const height = image.get_height();

  const canvas = document.createElement('canvas');
  canvas.width = width;
  canvas.height = height;

  const ctx = canvas.getContext('2d');
  const imageData = ctx.createImageData(width, height);
  await new Promise((resolve) => {
    image.display(imageData, (result) => resolve(result));
  });
  ctx.putImageData(imageData, 0, 0);

  return canvas;
}

heic2any

heic2any provides a simpler API that converts HEIC files directly to Blob objects in JPG, PNG, or GIF format.

import heic2any from 'heic2any';

async function convertHeicToJpg(heicBlob) {
  const jpgBlob = await heic2any({
    blob: heicBlob,
    toType: 'image/jpeg',
    quality: 0.85,
  });
  return jpgBlob;
}

heic2any uses libheif-js internally. It trades configurability for convenience.

Web Workers for Non-Blocking Conversion

HEIC decoding is CPU-intensive and will block the main thread. A 12 MP HEIC image takes 1-3 seconds to decode via WebAssembly. Use Web Workers to keep the UI responsive.

// converter.worker.js
import heic2any from 'heic2any';

self.onmessage = async (event) => {
  const { file, quality } = event.data;
  try {
    const result = await heic2any({
      blob: file,
      toType: 'image/jpeg',
      quality: quality || 0.85,
    });
    self.postMessage({ success: true, blob: result });
  } catch (error) {
    self.postMessage({ success: false, error: error.message });
  }
};

HEICify uses this exact pattern -- libheif-js running inside Web Workers -- to convert HEIC files entirely in the browser with no server uploads. Processing multiple files in parallel across multiple workers dramatically improves batch conversion throughput.

Image Delivery: Never Serve HEIC

Do not serve HEIC images to web browsers. With less than 20% browser support, HEIC will fail for the vast majority of users. Use a multi-format delivery strategy instead.

Recommended delivery formats

| Format | Browser Support | Best For | | --- | --- | --- | | WebP | 97%+ | Primary modern format for all images | | AVIF | 93%+ | Maximum compression, HDR content | | JPG | 100% | Universal fallback for photographs | | PNG | 100% | Fallback for images with transparency |

The picture element

The HTML <picture> element lets browsers select the optimal format from a list. Browsers pick the first source they support.

<picture>
  <source srcset="/images/photo.avif" type="image/avif" />
  <source srcset="/images/photo.webp" type="image/webp" />
  <img src="/images/photo.jpg" alt="Description" width="800" height="600" />
</picture>

This serves AVIF to Chrome 85+, Firefox 93+, and Safari 16.4+. Browsers without AVIF support get WebP. Browsers without WebP support (a negligible percentage in 2026) get JPG.

Content negotiation with Accept headers

Server-side content negotiation uses the Accept request header to detect format support. Browsers declare supported image formats in this header.

Accept: image/avif,image/webp,image/apng,image/*,*/*;q=0.8

A Node.js middleware example:

function negotiateImageFormat(req) {
  const accept = req.headers.accept || '';

  if (accept.includes('image/avif')) return 'avif';
  if (accept.includes('image/webp')) return 'webp';
  return 'jpg';
}

CDNs like Cloudflare, Fastly, and AWS CloudFront can automate this negotiation. Configure them to vary responses by the Accept header and serve pre-generated format variants.

Include Vary: Accept in the response headers when serving different formats from the same URL. This prevents caches from serving the wrong format to the wrong browser.

Vary: Accept
Content-Type: image/webp

Performance Considerations

Server-side conversion benchmarks

Processing a 12 MP HEIC file (4032 x 3024 pixels):

| Tool | Decode Time | Output to WebP | Output to JPG | | --- | --- | --- | --- | | Sharp | ~200 ms | ~350 ms total | ~300 ms total | | ImageMagick | ~400 ms | ~700 ms total | ~600 ms total | | libheif CLI | ~150 ms | N/A (decode only) | N/A (decode only) |

Client-side conversion benchmarks

WebAssembly HEIC decoding in the browser (12 MP file, modern desktop CPU):

| Library | Decode Time | Full Conversion | | --- | --- | --- | | libheif-js | ~1.5 s | ~2.0 s (to canvas) | | heic2any | ~1.8 s | ~2.5 s (to JPG blob) |

Client-side decoding is 5-10x slower than native server-side decoding. This is acceptable for single-file conversions. For batch processing, use multiple Web Workers to parallelize across CPU cores. Mobile devices are roughly 2-3x slower than desktops.

Memory usage

HEIC decoding requires holding the full uncompressed bitmap in memory. A 12 MP image at 4 bytes per pixel uses 48 MB of RAM. A 48 MP image uses 192 MB. Monitor memory usage when processing multiple files simultaneously in the browser.

Complete Upload Handling Example

A practical implementation that detects HEIC, converts to WebP, and returns a web-ready image:

async function handleImageUpload(file) {
  if (isHeicFile(file)) {
    // Convert HEIC to WebP via Sharp
    const buffer = Buffer.from(await file.arrayBuffer());
    const webpBuffer = await sharp(buffer)
      .resize(2048, 2048, { fit: 'inside', withoutEnlargement: true })
      .webp({ quality: 80 })
      .toBuffer();
    return { buffer: webpBuffer, mimeType: 'image/webp' };
  }

  // Non-HEIC images: optimize directly
  const buffer = Buffer.from(await file.arrayBuffer());
  const webpBuffer = await sharp(buffer)
    .resize(2048, 2048, { fit: 'inside', withoutEnlargement: true })
    .webp({ quality: 80 })
    .toBuffer();
  return { buffer: webpBuffer, mimeType: 'image/webp' };
}

Generate multiple format variants at upload time -- AVIF, WebP, and JPG -- to serve via the <picture> element or Accept header negotiation.

Key Takeaways

  1. Accept HEIC uploads -- iPhone users will send them. Detect by MIME type and file extension.
  2. Convert on ingest -- transform HEIC to WebP, AVIF, and JPG at upload time, not at delivery time.
  3. Never serve HEIC -- less than 20% browser support makes it unsuitable for image delivery.
  4. Use WebP as primary -- 97%+ browser support with strong compression. Add AVIF for maximum savings.
  5. Offload to Web Workers -- client-side HEIC decoding blocks the main thread for 1-3 seconds per image.
  6. Set Vary: Accept -- when serving multiple formats from the same URL, prevent cache poisoning.

For format comparison details, see HEIC vs WebP and HEIC vs AVIF. To convert HEIC files without installing software, use HEICify's HEIC to JPG converter or HEIC to PNG converter -- both process files entirely in your browser.

Frequently Asked Questions

Do web browsers support HEIC?
Only Safari supports HEIC natively (macOS 11+ and iOS 11+). Chrome, Firefox, Edge, and Opera do not decode HEIC. For web delivery, convert HEIC images to WebP, AVIF, or JPG. Use the picture element or Accept header content negotiation to serve the optimal format per browser.
How do I handle HEIC uploads in a web application?
Detect HEIC files by checking the file extension (.heic, .heics) or MIME type (image/heic, image/heif). Convert server-side using libheif, ImageMagick, or Sharp (Node.js). Alternatively, convert client-side using JavaScript libraries like heic2any or libheif-js to avoid server processing costs.
Should I serve HEIC images on my website?
No. HEIC has less than 20% browser support (Safari only). Serve WebP (97%+ support) as the primary modern format with JPG fallback. Use AVIF for maximum compression where browser support allows (Chrome 85+, Firefox 93+). HEIC should only appear in user upload flows, not in image delivery.
Can JavaScript decode HEIC files in the browser?
Yes. Libraries like libheif-js and heic2any use WebAssembly to decode HEIC files client-side. Performance is acceptable for individual files but slower than native decoding. HEICify uses this approach with Web Workers to convert HEIC files entirely in the browser without server uploads.

Related Guides

Ready to Convert Your Images?

Try our free, browser-based converter tools. No uploads required -- your files never leave your device.