Skip to content

Headless Image Loading

useImage() is a headless composable. Use it when you need the loading state machine and computed attributes but want to render your own markup.

ts
const {
  status, // Ref<'idle' | 'loading' | 'loaded' | 'error'>
  isLoaded, // ComputedRef<boolean>
  isError, // ComputedRef<boolean>
  imgAttrs, // ComputedRef<ImgAttrs> — ready to spread onto <img>
  observe, // (el: Ref<HTMLElement | null>) => void
  onImgLoad, // () => void — call from img @load
  onImgError, // () => void — call from img @error
} = useImage(options)

Options

src

string | SrcSet, optional. Image URL or format object.

widths

number[] · default: []. Widths for width-based (w) srcset generation.

densities

number[] | Record<number, string>, optional. Density descriptors (1x/2x/3x); list reuses src, map gives distinct files; takes precedence over widths, ignores sizes.

sizes

string, optional. sizes attribute value (width-based srcset only).

lazy

boolean · default: true. Enable IntersectionObserver.

rootMargin

string · default: "200px". IO rootMargin.

threshold

number · default: 0. IO threshold.

fit

ObjectFit · default: "cover". object-fit style.

maxRetries

number · default: 0. Max retry attempts on load failure.

retryDelay

number · default: 1000. Initial delay in ms; doubles each retry.

State machine

idle  →  loading  →  loaded
                  →  error
  • When lazy: true — transitions to loading when the observed element enters the viewport (internally driven by useLazyLoad())
  • When lazy: false — transitions to loading immediately after onMounted

Return value

status

Ref<ImageStatus>. Current loading state.

isLoaded

ComputedRef<boolean>. true when status === 'loaded'.

isError

ComputedRef<boolean>. true when status === 'error'.

imgAttrs

ComputedRef<object>. { src, srcset?, sizes?, style } — ready for v-bind.

observe

Function. Pass a Ref<HTMLElement> to start watching for intersection.

onImgLoad

Function. Call from <img @load> to advance to loaded.

onImgError

Function. Call from <img @error> to advance to error.

Example — custom render

vue
<script setup lang="ts">
import { ref, onMounted } from 'vue'
import { useImage } from '@macrulez/vue-image-kit'

const containerRef = ref<HTMLElement | null>(null)

const { status, isLoaded, imgAttrs, observe, onImgLoad, onImgError } = useImage({
  src: '/photo.jpg',
  widths: [400, 800, 1200],
  sizes: '(max-width: 768px) 100vw, 50vw',
})

onMounted(() => {
  observe(containerRef)
})
</script>

<template>
  <div ref="containerRef" class="image-wrapper">
    <div v-if="status === 'idle'" class="skeleton" />

    <img
      v-if="status === 'loading' || isLoaded"
      v-bind="imgAttrs"
      alt="Photo"
      :class="{ visible: isLoaded }"
      @load="onImgLoad"
      @error="onImgError"
    />

    <div v-if="status === 'error'" class="error-state">Failed to load</div>
  </div>
</template>

<style scoped>
img {
  opacity: 0;
  transition: opacity 0.3s;
}
img.visible {
  opacity: 1;
}
</style>