Blog Implementation Guides 8 min read

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 image fallbacknuxt image component@nuxt/imageplaceholder imagevue image fallback
Nuxt 3 Image Component Fallbacks and Error Handling

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.

Implementation tsx
// 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.

Implementation tsx
<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.

Implementation tsx
// 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.

Implementation tsx
// 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.

Implementation tsx
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.

Read the docs