vue-heic-image

Vue 3 HEIC/HEIF image preview and converter for the browser: HeicImage component, useHeicImage composable, PNG/JPEG/GIF output, TypeScript, iOS photos, heic2any.
935 4
Install
npm install vue-heic-image
Latest Version:2.0.0
Vue:3
License:MIT
Last Updated:Mar 24, 2026
Links: GitHub  ·  npm
Maintainer: parsajiravand

Vue HEIC Image

vue-heic-image helps Vue 3 apps show Apple HEIC/HEIF photos (iPhone, iPad) in the browser and convert them to PNG, JPEG, or GIF for uploads and sharing. It uses heic2any under the hood, exposes a HeicImage component and useHeicImage composable, and ships TypeScript types—ideal for Vite, Nuxt (client-only conversion), and any SPA that needs HEIC support without a server-side converter.

Live Demo

Check out the live demo: Vue HEIC Image Demo

Netlify Status

Features

  • Display HEIC images directly in the browser (with optional native display on Safari via preferNativeHeic)
  • Convert HEIC to PNG, JPEG, or GIF (powered by heic2any, loaded on demand)
  • Smart detection of HEIC files (no conversion for standard images)
  • Multi-frame HEIC: show all converted frames with the default layout or a custom #frames slot
  • Component events (converted, load, error) and common <img> passthrough props (loading, objectFit, etc.)
  • Helpers: downloadBlob, blobToFile
  • Quality control for JPEG output and GIF frame interval
  • TypeScript support
  • Use as a component or composable

Installation

npm install vue-heic-image
# or
yarn add vue-heic-image
# or
pnpm add vue-heic-image

Usage

Basic Component Usage

<template>
  <HeicImage src="path/to/image.heic" alt="My Image" />
</template>

<script setup>
import { HeicImage } from 'vue-heic-image';
</script>

Advanced Component Usage

<template>
  <HeicImage
    :src="imageFile"
    alt="My HEIC Image"
    :toType="'image/jpeg'"
    :quality="0.8"
    :multiple="true"
    :preferNativeHeic="true"
    object-fit="cover"
    class="my-image"
    @converted="onConverted"
    @load="onLoad"
    @error="onError"
  >
    <template #loading="{ isHeic }">
      <div class="loading">
        {{ isHeic ? 'Converting HEIC...' : 'Loading image...' }}
      </div>
    </template>

    <template #error="{ error }">
      <div class="error">
        Failed to load: {{ error.message }}
      </div>
    </template>
  </HeicImage>
</template>

<script setup lang="ts">
import type { HeicConvertedPayload } from 'vue-heic-image';

function onConverted(p: HeicConvertedPayload) {
  console.log(p.blobs, p.isHeic, p.usedNative);
}
function onLoad() {
  console.log('All default frame images finished loading');
}
function onError(e: Error | Event) {
  console.error(e);
}
</script>

Multi-frame slot

When :multiple="true" and the converter returns more than one image, you can customize layout with the frames slot. If you use this slot, you render the images yourself; the component does not emit load (handle loading in your own UI).

<template>
  <HeicImage :src="file" :multiple="true" alt="Frames">
    <template #frames="{ urls, blobs, count }">
      <div class="grid">
        <img v-for="(url, i) in urls" :key="i" :src="https://raw.githubusercontent.com/parsajiravand/vue-heic-image/HEAD/url" :alt="`Frame ${i + 1}`" />
      </div>
    </template>
  </HeicImage>
</template>

File Input Example

<template>
  <div>
    <input type="file" accept="image/*,.heic" @change="handleFileSelect" />

    <div v-if="selectedFile">
      <HeicImage
        :src="selectedFile"
        :toType="outputFormat"
        :quality="quality"
        class="preview-image"
      >
        <template #loading="{ isHeic }">
          <div class="status">
            {{ isHeic ? 'Converting HEIC...' : 'Loading...' }}
          </div>
        </template>
      </HeicImage>

      <div class="controls">
        <select v-model="outputFormat">
          <option value="image/png">PNG</option>
          <option value="image/jpeg">JPEG</option>
          <option value="image/gif">GIF</option>
        </select>

        <input
          v-if="outputFormat === 'image/jpeg'"
          type="range"
          v-model="quality"
          min="0"
          max="1"
          step="0.1"
        />
      </div>
    </div>
  </div>
</template>

<script setup lang="ts">
import { ref } from 'vue';
import { HeicImage } from 'vue-heic-image';
import type { HeicOutputType } from 'vue-heic-image';

const selectedFile = ref<File | null>(null);
const outputFormat = ref<HeicOutputType>('image/png');
const quality = ref(0.92);

const handleFileSelect = (event: Event) => {
  const file = (event.target as HTMLInputElement).files?.[0];
  if (file) selectedFile.value = file;
};
</script>

Using the Composable

import { useHeicImage } from 'vue-heic-image';

const { convertHeicToImage, isLoading, error, isHeic, usedNative } = useHeicImage({
  toType: 'image/png',
  quality: 0.92,
  multiple: false,
  gifInterval: 0.4,
  preferNativeHeic: false,
});

const handleFile = async (file: File) => {
  try {
    const result = await convertHeicToImage(file);

    if (isHeic.value) {
      console.log('HEIC processed; usedNative:', usedNative.value);
    }

    const url = URL.createObjectURL(Array.isArray(result) ? result[0] : result);
    return () => URL.revokeObjectURL(url);
  } catch (e) {
    console.error('Processing failed:', e);
  }
};

Helpers

import { downloadBlob, blobToFile } from 'vue-heic-image';

const file = blobToFile(blob, 'photo.png', 'image/png');
downloadBlob(file, file.name);

Nuxt and SSR

Conversion uses browser APIs and a dynamically imported heic2any. Do not run convertHeicToImage on the server. Wrap previews in ClientOnly (or equivalent) and call the composable only in client-side code:

<template>
  <ClientOnly>
    <HeicImage :src="file" alt="Preview" />
  </ClientOnly>
</template>

Props and options

Component props

Name Type Default Description
src File | Blob | string - Source image (HEIC or standard formats)
alt string - Alternative text for the image
toType 'image/png' | 'image/gif' | 'image/jpeg' 'image/png' Output format for HEIC conversion
quality number 0.92 JPEG quality (0–1), only for 'image/jpeg'
multiple boolean false Output multiple images for multi-frame HEIC
gifInterval number 0.4 Frame interval for GIF output (seconds)
preferNativeHeic boolean false Try Safari native HEIC display when toType is PNG and multiple is false; falls back to conversion
width number | string - Image width
height number | string - Image height
class string - CSS class names on <img>
objectFit CSS object-fit - Applied as inline style on <img>
loading, decoding, crossorigin, referrerpolicy, sizes, srcset, fetchpriority, draggable see HeicImageImgAttrs - Forwarded to <img>

Events

Event Payload Description
converted { blobs, isHeic, usedNative } After a successful resolve (single blob or array)
load - After all default-rendered frame images fire load (not used when you override the frames slot)
error Error | Event Composable conversion error or <img> error

Slot props

Loading

<template #loading="{ isHeic }">
  <!-- isHeic: true when the source is treated as HEIC -->
</template>

Error

<template #error="{ error }">
  <!-- error: Error -->
</template>

Frames (optional; when using this slot you control the DOM and load is not emitted by the component)

<template #frames="{ urls, blobs, count }">
  <!-- urls: string[] object URLs; blobs: Blob[]; count: number -->
</template>

Smart detection

The package detects HEIC images through MIME type and, when needed, file signature analysis. Non-HEIC images are passed through without calling heic2any.

Browser support

  • Modern browsers (Chrome, Firefox, Safari, Edge)
  • Requires fetch (for URL sources), URL.createObjectURL, and Image for optional native HEIC probing
  • iOS Safari 13.4+
  • Chrome/Edge 89+
  • Firefox 86+

License

MIT

Related Packages

vue3-image-viewer

`npm i vue3-image-viewer` 或者/or `yarn add vue3-image-viewer`

2,308
@yireen/squoosh-browser

An image compression tool run in browser while @squoosh/lib can not.

16,437 35
squoosh-browser-sense

An image compression tool run in browser while @squoosh/lib can not.

1,610 35
super-image-cropper

A powerful JavaScript image cropping library that supports GIF animations, PNG,...

27,932 23

Version History

Version Released Vue License
2.0.0 ^3.4.0 MIT
1.0.0 ^3.4.0 MIT