Nuxt 3 Image Component Fallbacks and Error Handling
Handle failed and missing images in Nuxt 3 with NuxtImg error events, composables, and fallback.pics placeholder URLs for dynamic image slots and CMS data.
Nuxt 3 projects that use @nuxt/image get optimized image delivery through NuxtImg and NuxtPicture components. Both emit an error event when the source URL fails — that is the primary hook for client-side fallback.
For Nuxt apps pulling dynamic content from APIs or a headless CMS, server-side fallback resolution in useAsyncData prevents broken image slots from reaching the browser in the first place.
Setup
Configure @nuxt/image for external domains
@nuxt/image is installed separately from Nuxt core and requires a provider configured in nuxt.config.ts. For simple external URLs and fallback.pics, the ipx provider or no provider at all works fine — the URLs are fully formed public endpoints.
Add fallback.pics and any other external image domains to the image.domains array so @nuxt/image can proxy or optimize them if needed.
// nuxt.config.ts
export default defineNuxtConfig({
modules: ['@nuxt/image'],
image: {
domains: ['fallback.pics', 'your-cdn.example.com'],
},
}); Error event
Handle NuxtImg load errors with the error event
NuxtImg emits an error event when the image fails to load. Bind a handler to swap src to a fallback URL. Use a ref to track the resolved source and prevent repeated error triggers.
The error event receives a native Event object. Use it to log which URL failed — useful for monitoring broken media in production dashboards.
<script setup>
const props = defineProps<{ imageSrc: string; alt: string }>();
const fallbackSrc = 'https://fallback.pics/api/v1/800x450/7C3AED/FFFFFF?text=Image+Unavailable';
const src = ref(props.imageSrc);
function onImageError() {
if (src.value !== fallbackSrc) src.value = fallbackSrc;
}
</script>
<template>
<NuxtImg
:src="src"
:alt="alt"
width="800"
height="450"
@error="onImageError"
/>
</template> Composable
Create a useImageFallback composable
Wrapping the fallback logic in a composable keeps components clean and makes the behavior testable in isolation. The composable returns a reactive src ref and an onError handler.
Accept the fallback URL as an optional parameter so the same composable covers different image slot sizes across the app.
// composables/useImageFallback.ts
export function useImageFallback(initial: string, fallback?: string) {
const DEFAULT = 'https://fallback.pics/api/v1/800x450?text=Not+Found';
const src = ref(initial);
const resolvedFallback = fallback ?? DEFAULT;
function onError() {
if (src.value !== resolvedFallback) src.value = resolvedFallback;
}
return { src, onError };
}
// In a component
const { src, onError } = useImageFallback(props.imageSrc); Server-side
Resolve fallbacks server-side in useAsyncData
When fetching content from an API, check for null or missing image fields during the useAsyncData call and substitute a fallback URL before the page renders. This prevents the broken-image icon from flashing on first paint.
Computing the fallback on the server also means social crawlers and link-preview bots see the correct og:image value — a client-side error handler does not help those cases.
// pages/blog/[slug].vue
const { data: post } = await useAsyncData('post', () =>
$fetch(`/api/posts/${route.params.slug}`)
);
const postImage = computed(() => {
if (!post.value) return 'https://fallback.pics/api/v1/1200x630?text=Loading';
return post.value.image
?? `https://fallback.pics/api/v1/1200x630.jpg?text=${encodeURIComponent(post.value.title)}`;
}); Avatar fallbacks
User avatars with initials from the avatar route
Avatar slots in user profiles, comment sections, and admin dashboards fail when no profile photo is set. The fallback.pics avatar route accepts a text parameter for initials.
Generate initials from the user's name server-side and embed them in the fallback URL. For consistent visual output, derive a background color from the user ID.
function avatarUrl(user: { name: string; image?: string }) {
if (user.image) return user.image;
const initials = user.name
.split(' ')
.map(n => n[0])
.join('')
.toUpperCase()
.slice(0, 2);
return `https://fallback.pics/api/v1/avatar/80?text=${initials}`;
} Performance
Prevent CLS with explicit dimensions on NuxtImg
Nuxt 3 with @nuxt/image injects width and height attributes automatically when you supply them as props. Always set these on NuxtImg — even when the image source might change to a fallback.
Keep fallback URL dimensions consistent with the main image dimensions. If the fallback.pics URL produces a different aspect ratio, you will get layout shift even after the error state is resolved.
Key takeaways
What to standardize before shipping
- Bind @error on NuxtImg and swap src via a ref; guard against infinite retries by comparing current src to the fallback first.
- Extract fallback logic into a useImageFallback composable to keep components clean and the behavior testable.
- Resolve null image fields server-side in useAsyncData so social crawlers receive a valid og:image on first fetch.
- Use /api/v1/avatar/{size}?text=JD for user avatars without a profile photo.
- Match fallback image dimensions to the slot dimensions to avoid layout shift after error recovery.
Production fallback layer
Use fallback.pics anywhere an image URL is accepted.
Start with one deterministic URL and standardize fallback behavior across your design system.