vue-heic-image
v2.0.0
Published
Vue 3 HEIC/HEIF image preview and converter for the browser: HeicImage component, useHeicImage composable, PNG/JPEG/GIF output, TypeScript, iOS photos, heic2any.
Maintainers
Readme
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
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
#framesslot - 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-imageUsage
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="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, andImagefor optional native HEIC probing - iOS Safari 13.4+
- Chrome/Edge 89+
- Firefox 86+
License
MIT
