VImage
The main component. Combines lazy loading, placeholder, format switching, and transitions in one element.
<VImage
src="/photo.jpg"
alt="Описание"
:width="1200"
:height="600"
blurhash="LEHV6nWB2yk8pyo0adR*.7kCMdnj"
placeholder="data:image/jpeg;base64,..."
:widths="[400, 800, 1200]"
sizes="(max-width: 768px) 100vw, 50vw"
:lazy="true"
root-margin="300px"
fit="cover"
@load="onLoad"
@error="onError"
>
<template #error>
<div class="my-error">Image failed to load</div>
</template>
</VImage>Props
src
string | SrcSet, optional when image is given. URL or object with format variants.
image
ImageMeta, optional. Build-time metadata (a CLI manifest entry or ?vik import) — seeds src/width/height/hazehash/blurhash/thumbhash/placeholder/sizes. Any explicit prop above overrides the matching field.
When a placeholders manifest is registered, its entry for this image's src fills in width/height/hazehash/blurhash/thumbhash the same way — after explicit props and image.
alt
string, required. alt attribute on the <img>. In dev builds, a suspicious value (missing, whitespace-only, or filename-shaped like "photo.jpg") logs a console.warn — a deliberate alt="" for a decorative image never triggers it.
width
number, optional. Intrinsic width; used to reserve aspect-ratio space.
height
number, optional. Intrinsic height; used to reserve aspect-ratio space.
hazehash
string, optional. HazeHash string from the optional hazehash package; decoded on demand into an in-memory canvas and turned into a CSS background on the element itself. The preferred placeholder: it takes precedence over blurhash, thumbhash and placeholder. If the package is not installed, the next placeholder is used.
blurhash
string, optional. BlurHash string; decoded into an in-memory canvas (never attached to the DOM) and turned into a CSS background on the element itself. Takes precedence over placeholder/thumbhash, but yields to hazehash — when blurhash is supplied and able to render, the LQIP/ThumbHash placeholder doesn't show at all (they're mutually exclusive, not layered).
thumbhash
string, optional. ThumbHash string; decoded to PNG data URL, used as blur-up placeholder.
placeholder
string, optional. Base64 LQIP or ThumbHash data URL; overrides thumbhash if both provided.
ssrPlaceholder
boolean · default: false. The server sends the cheap preview instead of the heavy image: the ready preview is the CSS background of the rendered <img>, and a lazy image is not downloaded until it nears the viewport — it is swapped in after hydration by the same IntersectionObserver (rootMargin) that drives every lazy image. Without the prop the server sends a plain <img src="…" loading="lazy">, which the browser may start downloading long before hydration, and the blur appears only after hydration.
For a lazy image the server <img> carries a transparent 1×1 pixel instead of the real src, and the real <img> goes into a <noscript> next to it, so search engines and visitors without JavaScript still get the full image. For an eager image (lazy="false" or priority) the real src stays and the preview is simply the background behind it — use that for the picture at the top of the page.
The preview must already be a data: URL: the placeholder prop, image.placeholder, or the one the Vite plugin registered for this src (imports: { preview }). A BlurHash or ThumbHash alone can't be decoded on the server (there is no canvas), so with only a hash the prop does nothing. It is also ignored with placeholderColor, placeholderMode="color" and placeholderMode="shimmer". After hydration the same preview stays under the image until it loads. See Preview in the server-rendered HTML.
placeholderMode
'blur' | 'color' | 'shimmer' · default: 'blur'. 'color' shows a solid color — the dominant color from the placeholders manifest, or the average color of thumbhash; 'shimmer' shows an animated skeleton (no hash needed).
placeholderColor
string, optional. Explicit solid CSS color placeholder; takes precedence and needs no decode.
widths
number[], optional. Pixel widths for automatic width-based (w) srcset generation.
densities
number[] | Record<number, string>, optional. Density descriptors (1x/2x/3x) for fixed-size images. List reuses src; map gives a distinct file per density. Takes precedence over widths, ignores sizes.
sizes
string, optional. sizes attribute passed to <img> (width-based srcset only).
breakpoints
BreakpointMap, optional. Local breakpoints (merged with global plugin breakpoints).
sources
ResponsiveSrc, optional. Breakpoint-key → URL (or { avif?, webp?, fallback }) map for art direction, optionally combined with format switching per breakpoint. An entry can also carry its own width/height and placeholder (blurhash, thumbhash, placeholder, placeholderColor), used while its breakpoint is active — see Per-breakpoint placeholders.
lazy
boolean · default: true. Enable IntersectionObserver lazy loading.
rootMargin
string · default: "200px". IO rootMargin — how far before the viewport loading starts.
threshold
number · default: 0. IO threshold — intersection ratio required to trigger.
fit
ObjectFit, optional. CSS object-fit value on the <img>. When set, it's always applied. Left unset, it defaults to "cover" — but only via an overridable CSS class, and only when the image box is actually constrained (layout="fill"/"fixed", or both width and height given); otherwise nothing is applied, since object-fit has no effect without a constrained box.
focal
FocalPoint, optional. Focal point { x, y } (fractions 0–1) → object-position; keeps the subject in frame when fit="cover" crops.
maxRetries
number · default: 0. Max retry attempts on load failure.
retryDelay
number · default: 1000. Initial delay in ms; doubles each retry (exponential backoff).
fetchpriority
'high' | 'low' | 'auto', optional. Browser fetch priority hint.
decoding
'async' | 'sync' | 'auto' · default: 'async'. Image decoding mode.
priority
boolean · default: false. Shorthand for the LCP/hero image — forces lazy=false, fetchpriority='high', decoding='sync'. Not automatic LCP detection (that isn't reliable pre-paint); mark the one image that matters instead of setting three props by hand.
respectSaveData
boolean · default: false. On a save-data connection: neutralizes priority (stays lazy) and downgrades src to the smallest URL available from densities/image.srcset. See Network-aware loading.
layout
'fixed' | 'responsive' | 'fill', optional. Sizing preset — applied directly to whichever element is currently showing the image (there's no wrapper, see Layout without a wrapper). Unset behaves identically to 'responsive' in every respect, including its auto-generated sizes. See Layout presets.
cdn
boolean | AutoLoaderConfig, optional. Opt-in: routes a string src through autoLoader() — detects the CDN from the URL and rewrites it, no manual adapter wiring. See CDN adapters → Auto CDN detection with VImage.
loader
'server', optional. Opt-in: routes a string src through the @macrulez/vue-image-kit/server on-demand handler via buildImageUrl(). See Self-hosted on-demand server → Wiring VImage to it.
loaderRoute
string · default: /_vik/image. Route override for loader="server" — takes precedence over the plugin/Nuxt-module serverRoute default.
fadeIn
boolean · default: false. Fades the whole box in (opacity 0→1, ~0.3s) shortly after mounting — not tied specifically to the moment the photo loads. The placeholder and the photo are painted on the same element (background + content), so fadeIn fades the whole box in as one unit rather than revealing a sharp photo through a fading blur — the instant the photo is ready, it covers the background placeholder underneath it. Off by default: the placeholder → photo swap is instant.
Events
@load
Event. Fired when the image finishes loading.
@error
Event. Fired when the image fails to load.
Slots
#error
Custom UI shown when the image fails to load. If omitted, a grey rectangle with a broken-image icon is shown.
Examples
Simple image with lazy loading:
<VImage src="/photo.jpg" alt="Landscape" />With blurhash and dimensions for aspect-ratio reservation:
<VImage
src="/photo.jpg"
alt="Landscape"
:width="1200"
:height="800"
blurhash="LEHV6nWB2yk8pyo0adR*.7kCMdnj"
/>WebP/AVIF with srcset and blur-up:
<VImage
:src="{ avif: '/photo.avif', webp: '/photo.webp', fallback: '/photo.jpg' }"
alt="Product"
:width="800"
:height="600"
placeholder="data:image/jpeg;base64,/9j/4AAQSkZJRgAB..."
:widths="[400, 800]"
sizes="(max-width: 640px) 100vw, 800px"
/>Disable lazy loading for above-the-fold images:
<VImage src="/hero.jpg" alt="Hero" :lazy="false" />LCP/hero image — priority instead of three separate props:
<VImage src="/hero.jpg" alt="Hero" :width="1600" :height="900" priority />From CLI/manifest output — no manual prop wiring:
<script setup lang="ts">
import meta from './photo.jpg?vik' // or: import { images } from './assets/images'
</script>
<template>
<!-- src/width/height/hazehash/blurhash/thumbhash/placeholder/sizes all come from meta -->
<VImage :image="meta" alt="Product photo" />
</template>Focal point — keep the subject in frame when cropping:
<!-- With fit="cover" the image is cropped to the box; focal decides which
part survives. { x: 0.5, y: 0.3 } favours the upper-middle (e.g. a face). -->
<VImage
src="/portrait.jpg"
alt="Team member"
:width="400"
:height="400"
fit="cover"
:focal="{ x: 0.5, y: 0.3 }"
/>Cheapest placeholder — a solid average color (no decode work, 0 bytes):
<!-- 'color' mode pulls the average RGBA straight from the ThumbHash header. -->
<VImage
src="/photo.jpg"
alt="Gallery item"
:width="600"
:height="400"
thumbhash="3OcRJYB4d3h/iIeHeEh3eIhw+j5n"
placeholder-mode="color"
/>
<!-- Or an explicit color you already know — needs no ThumbHash at all. -->
<VImage src="/photo.jpg" alt="Banner" placeholder-color="#1e3a8a" />Animated skeleton — when you have no hash at all:
<!-- A CSS shimmer sweep until the image loads. Respects prefers-reduced-motion. -->
<VImage src="/photo.jpg" alt="Card" :width="400" :height="300" placeholder-mode="shimmer" />Fading the box in on mount:
<!-- The placeholder → photo swap is instant by default. fadeIn adds a
light opacity fade-in for the whole box. -->
<VImage
src="/photo.jpg"
alt="Gallery item"
:width="600"
:height="400"
blurhash="LEHV6nWB2yk8pyo0adR*.7kCMdnj"
fade-in
/>Custom error slot:
<VImage src="/missing.jpg" alt="Missing">
<template #error>
<div class="placeholder">
<span>📷</span>
<p>Image unavailable</p>
</div>
</template>
</VImage>Handling events:
<script setup lang="ts">
function onLoad(e: Event) {
console.log('Image loaded', e)
}
function onError(e: Event) {
console.warn('Image failed', e)
}
</script>
<template>
<VImage src="/photo.jpg" alt="Photo" @load="onLoad" @error="onError" />
</template>