Skip to content

VImage ​

The main component. Combines lazy loading, placeholder, format switching, and transitions in one element.

vue
<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:

vue
<VImage src="/photo.jpg" alt="Landscape" />

With blurhash and dimensions for aspect-ratio reservation:

vue
<VImage
  src="/photo.jpg"
  alt="Landscape"
  :width="1200"
  :height="800"
  blurhash="LEHV6nWB2yk8pyo0adR*.7kCMdnj"
/>

WebP/AVIF with srcset and blur-up:

vue
<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:

vue
<VImage src="/hero.jpg" alt="Hero" :lazy="false" />

LCP/hero image — priority instead of three separate props:

vue
<VImage src="/hero.jpg" alt="Hero" :width="1600" :height="900" priority />

From CLI/manifest output — no manual prop wiring:

vue
<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:

vue
<!-- 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):

vue
<!-- '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:

vue
<!-- 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:

vue
<!-- 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:

vue
<VImage src="/missing.jpg" alt="Missing">
  <template #error>
    <div class="placeholder">
      <span>📷</span>
      <p>Image unavailable</p>
    </div>
  </template>
</VImage>

Handling events:

vue
<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>