11 KiB
Nuxt Image Reference
Last Updated: 2025-11
Check: @nuxt/image in package.json
Nuxt Image is an image optimization module that provides automatic image optimization, lazy loading, responsive images, and support for multiple image providers.
Installation & Setup
pnpm add @nuxt/image
nuxt.config.ts:
export default defineNuxtConfig({
modules: ["@nuxt/image"],
image: {
// Optional configuration
quality: 80,
formats: ["webp", "avif"],
// Image providers
providers: {
cloudinary: {
baseURL: "https://res.cloudinary.com/{your-cloud-name}/image/upload/",
},
},
},
})
Core Components
NuxtImg
Basic optimized image component:
<template>
<NuxtImg src="/images/hero.jpg" alt="Hero image" width="800" height="600" />
</template>
Common Props:
src- Image source (path or URL)alt- Alt text for accessibilitywidth/height- Dimensionsloading-"lazy"(default) or"eager"fit-"cover","contain","fill","inside","outside"format-"webp","avif","jpg","png"quality- Image quality (0-100)provider- Image provider to use
NuxtPicture
Responsive image with multiple formats:
<template>
<NuxtPicture
src="/images/hero.jpg"
:img-attrs="{ alt: 'Hero image', class: 'rounded-lg' }"
sizes="sm:100vw md:50vw lg:400px"
/>
</template>
Benefits:
- Automatically generates multiple formats (WebP, AVIF)
- Creates responsive srcset for different screen sizes
- Better browser compatibility with fallbacks
Common Patterns
Responsive Images
<template>
<NuxtPicture
src="/images/product.jpg"
:img-attrs="{ alt: 'Product image' }"
sizes="xs:100vw sm:100vw md:50vw lg:33vw"
:modifiers="{ fit: 'cover' }"
/>
</template>
Image with Loading States
<template>
<div class="relative">
<NuxtImg
src="/images/large.jpg"
alt="Large image"
loading="lazy"
placeholder
@load="imageLoaded = true"
/>
<div
v-if="!imageLoaded"
class="absolute inset-0 bg-gray-200 animate-pulse"
/>
</div>
</template>
<script setup lang="ts">
const imageLoaded = ref(false)
</script>
Background Images
<template>
<div
:style="{
backgroundImage: `url(${$img('/images/hero.jpg', { width: 1920, height: 1080 })})`,
backgroundSize: 'cover',
}"
class="h-96"
>
Content
</div>
</template>
Image with Different Formats
<template>
<NuxtPicture
src="/images/photo.jpg"
format="webp"
:img-attrs="{ alt: 'Photo', class: 'w-full' }"
/>
</template>
Fit Options
<template>
<div class="grid grid-cols-3 gap-4">
<!-- Cover: Crop to fill dimensions -->
<NuxtImg
src="/images/photo.jpg"
fit="cover"
width="300"
height="300"
alt="Cover"
/>
<!-- Contain: Fit within dimensions -->
<NuxtImg
src="/images/photo.jpg"
fit="contain"
width="300"
height="300"
alt="Contain"
/>
<!-- Fill: Stretch to fill -->
<NuxtImg
src="/images/photo.jpg"
fit="fill"
width="300"
height="300"
alt="Fill"
/>
</div>
</template>
Image Providers
Local Provider (Default)
For images in public/ directory:
<template>
<NuxtImg src="/images/local.jpg" />
</template>
External URLs
<template>
<NuxtImg src="https://example.com/image.jpg" provider="cloudinary" />
</template>
Cloudinary
// nuxt.config.ts
export default defineNuxtConfig({
image: {
cloudinary: {
baseURL: "https://res.cloudinary.com/{your-cloud-name}/image/upload/",
},
},
})
<template>
<NuxtImg provider="cloudinary" src="sample.jpg" width="600" height="400" />
</template>
Vercel / Netlify
Automatically detected and configured when deployed:
// nuxt.config.ts
export default defineNuxtConfig({
image: {
provider: "vercel", // or 'netlify'
},
})
Custom Provider
// nuxt.config.ts
export default defineNuxtConfig({
image: {
providers: {
custom: {
provider: "~/providers/custom-provider.ts",
options: {
baseURL: "https://cdn.example.com",
},
},
},
},
})
// providers/custom-provider.ts
import { joinURL } from "ufo"
import type { ProviderGetImage } from "@nuxt/image"
export const getImage: ProviderGetImage = (src, { modifiers, baseURL }) => {
const { width, height, format, quality } = modifiers
const url = joinURL(baseURL, src)
return {
url: `${url}?w=${width}&h=${height}&fm=${format}&q=${quality}`,
}
}
Composables
$img Helper
Generate image URLs programmatically:
<script setup lang="ts">
const { $img } = useNuxtApp()
// Generate optimized URL
const imageUrl = $img("/images/photo.jpg", {
width: 800,
height: 600,
format: "webp",
quality: 80,
})
// Use in v-bind or computed
const backgroundImage = computed(() =>
$img("/images/hero.jpg", { width: 1920 }),
)
</script>
<template>
<div :style="{ backgroundImage: `url(${backgroundImage})` }">Content</div>
</template>
Performance Optimization
Lazy Loading (Default)
Images are lazy-loaded by default:
<template>
<!-- Lazy loaded (default) -->
<NuxtImg src="/images/photo.jpg" loading="lazy" />
<!-- Eager load for above-the-fold images -->
<NuxtImg src="/images/hero.jpg" loading="eager" />
</template>
Preload Critical Images
<script setup lang="ts">
useHead({
link: [
{
rel: "preload",
as: "image",
href: "/images/hero.jpg",
type: "image/jpeg",
},
],
})
</script>
<template>
<NuxtImg src="/images/hero.jpg" loading="eager" />
</template>
Placeholder / Blur
<template>
<NuxtImg
src="/images/large.jpg"
placeholder
alt="Image with blur placeholder"
/>
</template>
Image Sizes
Specify responsive sizes for optimal loading:
<template>
<NuxtPicture
src="/images/responsive.jpg"
sizes="xs:100vw sm:100vw md:50vw lg:400px xl:400px"
:img-attrs="{ alt: 'Responsive image' }"
/>
</template>
Advanced Usage
Modifiers Object
Pass multiple modifiers:
<script setup lang="ts">
const imageModifiers = {
width: 800,
height: 600,
fit: "cover",
format: "webp",
quality: 85,
}
</script>
<template>
<NuxtImg src="/images/photo.jpg" :modifiers="imageModifiers" alt="Photo" />
</template>
Dynamic Sources
<script setup lang="ts">
const images = ref([
{ id: 1, src: "/images/photo1.jpg", alt: "Photo 1" },
{ id: 2, src: "/images/photo2.jpg", alt: "Photo 2" },
{ id: 3, src: "/images/photo3.jpg", alt: "Photo 3" },
])
</script>
<template>
<div class="grid grid-cols-3 gap-4">
<NuxtImg
v-for="image of images"
:key="image.id"
:src="image.src"
:alt="image.alt"
width="400"
height="300"
fit="cover"
/>
</div>
</template>
Image Gallery
<template>
<div class="grid grid-cols-2 md:grid-cols-3 lg:grid-cols-4 gap-4">
<NuxtPicture
v-for="(image, index) of gallery"
:key="index"
:src="image.src"
:img-attrs="{
alt: image.alt,
class: 'w-full h-64 object-cover rounded-lg cursor-pointer',
}"
sizes="sm:50vw md:33vw lg:25vw"
@click="openLightbox(image)"
/>
</div>
</template>
<script setup lang="ts">
const gallery = ref([
{ src: "/images/gallery1.jpg", alt: "Gallery 1" },
{ src: "/images/gallery2.jpg", alt: "Gallery 2" },
{ src: "/images/gallery3.jpg", alt: "Gallery 3" },
])
function openLightbox(image: any) {
// Handle lightbox
}
</script>
Art Direction
Different images for different screen sizes:
<template>
<picture>
<source
media="(min-width: 1024px)"
:srcset="$img('/images/hero-desktop.jpg', { width: 1920 })"
/>
<source
media="(min-width: 768px)"
:srcset="$img('/images/hero-tablet.jpg', { width: 1024 })"
/>
<NuxtImg src="/images/hero-mobile.jpg" alt="Hero" width="768" />
</picture>
</template>
Configuration Reference
Global Configuration
// nuxt.config.ts
export default defineNuxtConfig({
image: {
// Default quality
quality: 80,
// Default formats
formats: ["webp", "avif", "jpg"],
// Image sizes for responsive images
screens: {
xs: 320,
sm: 640,
md: 768,
lg: 1024,
xl: 1280,
xxl: 1536,
"2xl": 1536,
},
// Provider configuration
provider: "cloudinary",
providers: {
cloudinary: {
baseURL: "https://res.cloudinary.com/{cloud-name}/image/upload/",
},
},
// IPX options (local provider)
ipx: {
maxAge: 60 * 60 * 24 * 365, // 1 year cache
},
// Presets
presets: {
avatar: {
modifiers: {
format: "webp",
width: 100,
height: 100,
fit: "cover",
},
},
thumbnail: {
modifiers: {
format: "webp",
width: 300,
height: 200,
fit: "cover",
},
},
},
},
})
Using Presets
<template>
<NuxtImg src="/images/user.jpg" preset="avatar" alt="User avatar" />
<NuxtImg
src="/images/product.jpg"
preset="thumbnail"
alt="Product thumbnail"
/>
</template>
Best Practices
- Always include alt text - Essential for accessibility
- Use NuxtPicture for hero images - Better format support and responsiveness
- Specify dimensions - Prevents layout shift
- Lazy load by default - Except above-the-fold images
- Use appropriate fit -
coverfor thumbnails,containfor products - Optimize quality - 80-85 is usually sufficient
- Leverage providers - Use CDN providers for external images
- Use presets - Define common image styles once
- Test on slow networks - Verify lazy loading and placeholders work
- Prefer WebP/AVIF - Modern formats for better compression
Troubleshooting
Images Not Optimizing
Check:
@nuxt/imageis innuxt.config.tsmodules- Images are in
public/directory for local provider - Provider is correctly configured
- Development server was restarted after config changes
Images Not Loading
- Verify src path is correct
- Check provider baseURL configuration
- Ensure CORS is configured for external images
- Check network tab for 404/403 errors
Poor Performance
- Enable lazy loading (default)
- Use appropriate image sizes
- Implement placeholders
- Use WebP/AVIF formats
- Configure CDN caching
Layout Shift
Always specify width and height:
<template>
<!-- Bad: No dimensions -->
<NuxtImg src="/images/photo.jpg" />
<!-- Good: Dimensions specified -->
<NuxtImg src="/images/photo.jpg" width="800" height="600" />
</template>
Official Resources
- Documentation: https://image.nuxt.com
- Providers: https://image.nuxt.com/providers
- API Reference: https://image.nuxt.com/api
- GitHub: https://github.com/nuxt/image
Note: Always verify provider-specific features with official documentation. Image optimization strategies may vary by provider.