image
image copied to clipboard
Setting fallback image on 404 respons after fetch
Hi
I'm not really sure how this is supposed to work? There is nothing in the documentation that shows how to do it. It says that you can use Native events such as load or error.
I have tried add a fallback image when the response comes back as 404 direct on the NuxtImg components like so:
<NuxtImg :src="item.ImageURL" width="120" height="120" @error="$event.target.src = fallbackImg" />
but without luck 😢
I've also tried making a function that returns the url to another fallback image but no luck.
It feels like the error event is emitted long after all the images are looped? Or Is there a way to catch it before?
Hey @danielohling
Have you found a solution, I'm facing the same issue. Still exploring
Thanks
Same issue here
Hey @danielohling
Have you found a solution, I'm facing the same issue. Still exploring
Thanks
This is my solution:
<script lang="ts">
import { defineComponent } from 'vue'
import { imgProps } from '@nuxt/image/dist/runtime/components/nuxt-img'
export default defineComponent({
props: imgProps,
setup(props) {
const { fallbackImage } = useAppConfig()
const src = ref(props.src)
watch(
() => props.src,
value => {
src.value = value
},
)
return () =>
h(resolveComponent('NuxtImg'), {
...props,
src: src.value,
onError: () => {
src.value = fallbackImage
},
})
},
})
</script>
You could use
<NuxtImg :src="original.png" :placeholder="placeholder.png" />
as the placeholder acts like a fallback too if the image fails to load. (Got it from the Nuxt Discord)
You could use
<NuxtImg :src="original.png" :placeholder="placeholder.png" />
as the placeholder acts like a fallback too if the image fails to load. (Got it from the Nuxt Discord)
This only works for NuxtImg
and not NuxtPicture
. Also the placeholder
witll ALWAYS be loaded, even if not needed.
In my case I want to show the original image if the optimization failed for some reason. If I would use placeholder, it would always load the original image as well. To only show the fallback, if an actual eerror happens, I created my own components, wrapping NuxtImg
and NuxtPicture
.
CustomNuxtImg.vue
:
<template>
<nuxt-img v-if="!hasError || !fallback" v-bind="propsWithoutFallbackAndSrc" :src="src" @error="handleError" @load="emit('load', $event)" />
<img v-else v-bind="propsWithoutFallbackAndSrc" :src="fallback" />
</template>
<script lang="ts" setup>
import type { NuxtImgProps } from "@base/modules/image/types/nuxt-image"
/*
* Small wrapper over NuxtImg
* Supports fallback to default img tag when nuxt-img was unable to optimize the image.
* This could happen if an image file is corrupted (e.g. wrongly saved by an image editing software)
* However, providing a fallback is always a good practise
*/
interface Emits {
(e: "error", data: any): void
(e: "load", data: any): void
}
interface Props extends /* @vue-ignore */ NuxtImgProps {
src?: string
fallback?: string
}
const emit = defineEmits<Emits>()
const props = defineProps<Props>()
const attrs = useAttrs()
const propsWithoutFallbackAndSrc = computed(() => {
const { fallback, src, ...restProps } = props
return { ...attrs, ...restProps }
})
const hasError = ref(false)
const handleError = (data: any) => {
emit("error", data)
hasError.value = true
}
</script>
CustomNuxtPicture.vue
:
<template>
<nuxt-picture v-if="!hasError || !fallback" v-bind="propsWithoutFallbackAndSrc" :src="src" :imgAttrs="{ onError: handleError }" @load="emit('load', $event)" />
<img v-else v-bind="propsWithoutFallbackAndSrc" :src="fallback" />
</template>
<script lang="ts" setup>
import type { NuxtPictureProps } from "@base/modules/image/types/nuxt-image"
/*
* Small wrapper over NuxtPicture
* Supports fallback to default img tag when nuxt-img was unable to optimize the image.
* This could happen if an image file is corrupted (e.g. wrongly saved by an image editing software)
* However, providing a fallback is always a good practise
* */
interface Emits {
(e: "error", data: any): void
(e: "load", data: any): void
}
interface Props extends /* @vue-ignore */ NuxtPictureProps {
src?: string
fallback?: string
}
const emit = defineEmits<Emits>()
const props = defineProps<Props>()
const attrs = useAttrs()
const propsWithoutFallbackAndSrc = computed(() => {
const { fallback, src, ...restProps } = props
return { ...attrs, ...restProps }
})
const hasError = ref(false)
const handleError = (data: any) => {
emit("error", data)
hasError.value = true
}
</script>
Would really appreciate an official API.
@danielroe
I was just looking for the same feature. Here in the Nuxt Movies example there is exactly a use case where the HD
image is not present and we could use the SD
one as fallback.
Go under the videos tab here: https://movies.nuxt.space/tv/63770
/youtube/vi/nWufb0qL1xU/maxresdefault.jpg
-> 404 ❌
/youtube/vi/nWufb0qL1xU/hqdefault.jpg
-> 200 ✅
This is the componente Card.vue
<script setup lang="ts">
import type { Video } from '~/types'
const props = defineProps<{
item: Video
}>()
const showModal = useIframeModal()
function play() {
return showModal(getVideoLink(props.item)!)
}
</script>
<template>
<button pb2 text-left @click="play()">
<div
block bg-gray4:10 p1 flex
class="aspect-16/9"
transition duration-400 relative
hover="scale-102 z10"
>
<NuxtImg
:src="`/youtube/vi/${item.key}/maxresdefault.jpg`"
width="400"
height="600"
format="webp"
:alt="props.item.name"
w-full h-full object-cover
/>
<div flex w-full h-full absolute inset-0 op20 hover:op100 transition>
<div i-ph-play ma text-3xl />
</div>
</div>
<div mt-2>
{{ props.item.name }}
</div>
<div op60 text-sm>
{{ props.item.type }}
</div>
</button>
</template>