image icon indicating copy to clipboard operation
image copied to clipboard

Lazy Loading Fails with NuxtImg Custom Slot

Open danekslama opened this issue 5 months ago • 0 comments

Description of the Issue

When the "custom" prop or a placeholder is set, const img = new Image() is immediately triggered to check when the image is ready. This causes immediate loading, preventing lazy loading from working. This behavior is understandable, as NuxtImg in this scenario primarily serves as a provider for optimizations.

The core question is: should we implement lazy loading functionality for this case directly within NuxtImg, or is this a more appropriate scenario for using useImage?


Possible Solution Using useImage

<template>
  <span
    v-if="!isVisible"
    ref="trackerRef"
  />

  <slot
    v-else-if="!isLoaded"
    name="placeholder"
  />

  <Transition
    appear
    v-bind="ThemeTransitions.fade"
  >
    <img
      v-if="isLoaded"
      :src="imageUrl"
      :srcset="imageSrcset"
      :alt="props.alt"
      v-bind="$attrs"
    >
  </Transition>
</template>

<script setup lang="ts">
defineOptions({ inheritAttrs: false })

const trackerRef = ref<HTMLSpanElement | null>(null)

const isLoaded = ref(false)
const isVisible = ref(false)

const props = defineProps<{ src: string, alt?: string, preload?: boolean }>()
const $img = useImage()
const imageUrl = $img(props.src, {})
const imageSrcset = $img.getSizes(props.src, {}).srcset

let observer: IntersectionObserver | null = null

onMounted(() => {
  if (trackerRef.value && !isLoaded.value) {
    observer = new IntersectionObserver(([entry]) => {
      if (entry.isIntersecting) {
        isVisible.value = true
        observer?.disconnect()

        const img = new window.Image()
        img.src = imageUrl
        img.onload = () => {
          isLoaded.value = true
        }
      }
    })
    observer.observe(trackerRef.value)
  }
})

onUnmounted(() => {
  observer?.disconnect()
})
</script>

danekslama avatar Jul 31 '25 08:07 danekslama