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.
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 toloadingwhen the observed element enters the viewport (internally driven byuseLazyLoad()) - When
lazy: false— transitions toloadingimmediately afteronMounted
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
<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>