Adding Images To Markdown With Nuxt Content Module
How to add images to Nuxt Content blog posts using @nuxt/image for automatic optimization and lazy loading.
When I first started building this blog with Nuxt Content, I hit a wall trying to add images to my markdown posts. Standard markdown image syntax didn't work the way I expected. I needed a way to optimize images, add lazy loading, and make them work seamlessly in markdown.
The modern solution? Use @nuxt/image with images in the static/ folder. It handles optimization, lazy loading, and responsive images automatically.
The Modern Approach: Static Folder + @nuxt/image
After trying various approaches, here's what actually works in production:
Where to Store Images
Put your images in the static/blog/ folder (not assets/):
static/
blog/
hero-image.webp
screenshot.png
diagram.svg
The static folder serves files directly without webpack processing.
Using Images in Markdown Content
In your Nuxt Content markdown files, use regular <img> tags:
<img src="/blog/screenshot.png" alt="Project screenshot" loading="lazy" />
Why not <nuxt-img>? The <nuxt-img> component doesn't work inside Nuxt Content markdown files. It only works in Vue templates and components.
Using @nuxt/image in Vue Templates
For hero images and thumbnails in your Vue components, you can use <nuxt-img> with responsive optimization:
<template>
<nuxt-img :src="`/blog${article.thumbnail}`" :alt="article.title" loading="lazy" sizes="sm:100vw md:100vw lg:900px" />
</template>
Install the module:
npm install @nuxt/image
Add it to nuxt.config.js:
export default {
modules: ['@nuxt/image'],
image: {
screens: {
sm: 640,
md: 768,
lg: 1024,
xl: 1920
}
}
}
This gives you automatic image optimization and responsive sizes for hero images and preview thumbnails in your Vue templates.
Why This Works Better
Before (old v-img approach):
- Images in
assets/folder - Webpack bundling required custom component
- Manual optimization needed
- Complex setup
After (static folder + @nuxt/image):
- Images in
static/folder - simple and direct - Regular
<img>tags in markdown - no custom component needed - @nuxt/image optimization for hero images in Vue templates
- Responsive sizes where it matters (thumbnails, hero images)
Pre-Optimize Your Images
Since you're using regular <img> tags in markdown, pre-optimize your images before adding them to static/blog/:
# Resize to 1920px max width
sips -Z 1920 input.png --out temp.png
# Convert to WebP with 85% quality
cwebp -q 85 temp.png -o static/blog/output.webp
# Clean up
rm temp.png
This gives you the best of both worlds:
- Manually optimized images (100-200KB instead of 5-6MB)
- @nuxt/image responsive optimization for hero/thumbnail images in Vue templates
- Simple
<img>tags in markdown that just work
The Old Way (Legacy)
Before this approach, you needed a custom VImg component with webpack's require() to bundle images from the assets/ folder. That approach still works but is overcomplicated.
The static folder approach is simpler, more reliable, and works consistently across development and production builds.
Performance Results
After switching to this approach:
- Hero images serve responsive sizes automatically (640px on mobile, 1920px on desktop)
- Manually optimized WebP images in content (63-185KB each, 98% smaller than original PNGs)
- Native lazy loading on all images
- Better Core Web Vitals scores
Keep it simple: optimize images manually, store them in static/blog/, use regular <img> tags in markdown.