Universal image rendering engine for Vue 3 — supports JPEG, PNG, WebP, GIF, HEIC, and AVIF with WASM fallback and zero main-thread blocking.
- Format Detection — Binary signature detection (never file extensions) for JPEG, PNG, WebP, GIF, HEIC, HEIF, AVIF
- Capability Probing — Pixel-verified native decode testing to determine browser support
- WASM Fallback — Automatic fallback to libheif-js for HEIC/AVIF on unsupported browsers
- Web Worker Decoding — Decode off the main thread via a pre-spawned worker pool
- EXIF Auto-Orient — Automatic JPEG EXIF orientation handling
- GPU Rendering — Uses ImageBitmap + drawImage for GPU-accelerated canvas rendering
- Vue 3 Integration —
useImagecomposable and<UniversalImage>component - SSR Safe — All browser APIs guarded for Nuxt/SSR environments
- Tree-Shakable — ESM-only with
sideEffects: false
npm install vue-image-parserHEIC/AVIF support via libheif-js is included out of the box.
<script setup>
import { useImage } from 'vue-image-parser'
const { image, loading, error } = useImage('/photos/example.heic')
</script>
<template>
<div v-if="loading">Loading...</div>
<div v-else-if="error">{{ error.message }}</div>
<canvas v-else ref="canvas" />
</template><script setup>
import { UniversalImage } from 'vue-image-parser'
</script>
<template>
<UniversalImage src="/photos/example.avif" :max-dimension="1024" />
</template>import { createApp } from 'vue'
import { ImageParserPlugin } from 'vue-image-parser'
const app = createApp(App)
app.use(ImageParserPlugin)import { loadImage, renderImage, detectFormat, disposeEngine } from 'vue-image-parser'
// Load and decode
const decoded = await loadImage('/photo.heic', {
strategy: 'auto', // 'auto' | 'native' | 'wasm'
maxDimension: 2048,
timeout: 30000,
})
// Render to canvas
const canvas = document.getElementById('canvas') as HTMLCanvasElement
renderImage(canvas, decoded, { fit: 'contain' })
// Detect format without decoding
const format = detectFormat(buffer)
// Clean up when done
decoded.dispose()
disposeEngine()| Function | Description |
|---|---|
loadImage(source, options?) |
Load and decode an image from URL, File, Blob, or ArrayBuffer |
renderImage(target, image, options?) |
Render a decoded image onto a canvas or HTML element |
detectFormat(buffer) |
Detect image format from binary signature |
detectFormatFromBlob(blob) |
Detect format from a Blob (reads first 64 bytes) |
warmup(formats) |
Pre-initialize WASM codecs for given formats |
disposeEngine() |
Release all resources (worker pool, codecs, registries) |
| Option | Type | Default | Description |
|---|---|---|---|
strategy |
'auto' | 'native' | 'wasm' |
'auto' |
Decoding strategy |
signal |
AbortSignal |
— | Cancellation signal |
timeout |
number |
30000 |
Timeout in milliseconds |
maxDimension |
number |
— | Max width/height for downsampling |
maxFileSize |
number |
104857600 |
Max file size in bytes (100 MB) |
autoOrient |
boolean |
true |
Auto-orient based on EXIF |
onProgress |
(p: number) => void |
— | Progress callback (0.0-1.0) |
| Option | Type | Default | Description |
|---|---|---|---|
fit |
'contain' | 'cover' | 'fill' | 'none' | 'scale-down' |
'contain' |
Object-fit behavior |
width |
number |
image width | Target width |
height |
number |
image height | Target height |
background |
string |
— | Background color for letterboxing |
dpr |
number |
devicePixelRatio |
Device pixel ratio |
| Function | Description |
|---|---|
toDataURL(image, mimeType?) |
Convert decoded image to data URL |
toBlobURL(image, mimeType?) |
Convert decoded image to blob URL |
toImageBitmap(image) |
Convert decoded image to ImageBitmap |
All errors extend ImageParserError with a machine-readable code:
| Error | Code(s) | When |
|---|---|---|
FormatDetectionError |
FORMAT_DETECTION_FAILED |
Unrecognized binary signature |
CodecError |
DECODE_FAILED, HEIC_DECODE_FAILED, AVIF_DECODE_FAILED |
Decode failure |
FetchError |
FETCH_FAILED, FILE_TOO_LARGE |
Network or file size error |
TimeoutError |
FETCH_TIMEOUT |
Operation exceeded timeout |
AbortError |
ABORTED |
Cancelled via AbortSignal |
WorkerError |
WORKER_CRASHED |
Web Worker failure |
| Format | Native | WASM Fallback |
|---|---|---|
| JPEG | All browsers | — |
| PNG | All browsers | — |
| GIF | All browsers | — |
| WebP | All modern browsers | — |
| HEIC/HEIF | Safari 17+ | libheif-js |
| AVIF | Chrome 85+, Firefox 93+ | libheif-js |
- Chrome 85+
- Firefox 93+
- Safari 16.4+ (OffscreenCanvas)
- Edge 85+
Older browsers work with reduced functionality (main-thread decoding, no OffscreenCanvas).
Decoded images hold GPU resources (ImageBitmaps) and pixel buffers. Always dispose them when done:
const decoded = await loadImage('/photo.heic')
try {
renderImage(canvas, decoded)
} finally {
decoded.dispose()
}In long-lived apps (SPAs), call disposeEngine() when your image-handling view is torn down to release the worker pool and codec instances:
import { onBeforeUnmount } from 'vue'
import { disposeEngine } from 'vue-image-parser'
onBeforeUnmount(() => {
disposeEngine()
})The useImage() composable handles disposal automatically on component unmount.
All errors extend ImageParserError with a machine-readable code for programmatic handling:
import { loadImage, ImageParserError, ErrorCodes } from 'vue-image-parser'
try {
const decoded = await loadImage(url)
} catch (error) {
if (error instanceof ImageParserError) {
switch (error.code) {
case ErrorCodes.FORMAT_DETECTION_FAILED:
console.warn('Unsupported image format')
break
case ErrorCodes.FETCH_TIMEOUT:
console.warn('Image took too long to load')
break
case ErrorCodes.FILE_TOO_LARGE:
console.warn('Image exceeds size limit')
break
default:
console.error('Image error:', error.message)
}
}
}All browser APIs are guarded behind isBrowser() checks. Use onMounted() or <ClientOnly> to defer image operations:
<script setup>
import { onMounted } from 'vue'
import { loadImage } from 'vue-image-parser'
onMounted(async () => {
const decoded = await loadImage('/photo.heic')
// ...
})
</script>Contributions are welcome! Please read the Contributing Guide and Code of Conduct before submitting a PR.