Migrating from Nuxt Content Assets to Nuxt Content V3
The Challenge
When migrating to Nuxt Content V3 for LLM compatibility, we face two main challenges:
- Loss of asset management from Nuxt Content Assets
- Issues with relative image paths in markdown files, especially with URLs ending with or without trailing slashes
Understanding the Problems
Asset Management
In Nuxt Content V2, assets were automatically handled within content folders. V3 requires manual asset management.
Path Resolution Issues
URLs with or without trailing slashes can cause problems with relative image paths, especially in production environments like Vercel. We need to handle these cases specifically.
Implementation
1. Asset Copying Setup
First, update your
nuxt.config.ts
:export default defineNuxtConfig({
nitro: {
plugins: ['~/scripts/copy-content-images']
}
})
Create
scripts/copy-content-images.ts
:import { promises as fs } from 'fs'
import { join, relative, dirname } from 'path'
// Helper to check file existence
async function exists(path: string) {
try {
await fs.access(path)
return true
} catch {
return false
}
}
async function copyPngFiles(sourcePath: string, targetPath: string) {
try {
const entries = await fs.readdir(sourcePath, { withFileTypes: true })
for (const entry of entries) {
const srcPath = join(sourcePath, entry.name)
if (entry.isDirectory()) {
await copyPngFiles(srcPath, targetPath)
} else if (entry.name.toLowerCase().endsWith('.png')) {
const relPath = relative('./content', srcPath)
const destPath = join(targetPath, relPath)
await fs.mkdir(dirname(destPath), { recursive: true })
await fs.copyFile(srcPath, destPath)
console.log(`β Asset copied: ${relPath}`)
}
}
} catch (error) {
console.error('β Error copying assets:', error)
}
}
export default defineNitroPlugin(async () => {
const contentDir = './content'
const outputDir = './.output/public'
if (await exists(contentDir)) {
console.log('π Starting asset migration...')
await copyPngFiles(contentDir, outputDir)
console.log('β¨ Asset migration complete')
}
})
2. Server Middleware for Development
Create a server middleware to handle image serving in development:
// server/middleware/serve-images.ts
import { join } from 'node:path'
import { readFileSync, existsSync } from 'node:fs'
import { defineEventHandler } from 'h3'
export default defineEventHandler((event) => {
const path = event.path
// Check if the request is for an image
if (path.match(/\.(png|jpg|jpeg|gif|webp)$/)) {
const filePath = join(process.cwd(), 'content', path)
if (existsSync(filePath)) {
const file = readFileSync(filePath)
const mimeType = getMimeType(filePath)
return new Response(file, {
headers: { 'Content-Type': mimeType },
})
}
}
})
This middleware:
- Intercepts image requests
- Serves them directly from the content directory
- Handles multiple image formats
- Works seamlessly in development
3. Path Resolution Fix
To handle relative paths correctly, we implement a transform function when fetching content:
const { data: post } = await useAsyncData('page-' + path, async () => {
return queryCollection('content').path(path).first();
}, {
transform: (data) => {
data.body.value = updateImageSources(data.body.value, data.path);
return data;
}
})
The transform function recursively processes the markdown content array and converts relative paths to absolute ones.
How It Works
- The Nitro plugin handles asset copying during build
- The transform function processes markdown content to fix relative paths
- Special handling for Vercel deployments through environment checks
Benefits
- Maintains organized content structure
- Automatic asset copying during build
- Compatible with Nuxt Content V3 and Nuxt LLM
- Zero runtime overhead
Current Limitations
- Path resolution might still need manual attention in some edge cases
- Development and production environments might behave differently
- Vercel deployments require additional configuration
- Manual rebuild needed when content changes
Best Practices
- Always use consistent path formats in markdown files
- Test both with and without trailing slashes
- Verify image paths in both development and production
- Consider using absolute paths when possible
Future Improvements
- Implement more robust path resolution
- Create a unified behavior between development and production
- Add automated testing for path resolution
- Consider implementing a caching layer
- Use a transformer from nuxt content
Resources
2025-03-01 22:002025-03-02T09:00:00.000ZNuxt ContentNuxt.jsMigration GuideAsset Management