Optimization Best Practices

Optimizing your static assets is crucial for performance, user experience, and SEO in Nuxt applications.

Image Optimization

Size Optimization

Compression Levels:

  • JPEG: Quality 75-85 for photos
  • WebP: Quality 80-85 for web delivery
  • PNG: Use tools like pngquant for lossy compression

Tools:

# ImageMagick
convert input.jpg -quality 85 -strip output.jpg

# pngquant
pngquant --quality=65-80 input.png -o output.png

# Sharp (Node.js)
sharp('input.jpg')
  .jpeg({ quality: 85 })
  .toFile('output.jpg');

Responsive Images

Provide multiple sizes for different viewports:

<template>
  <NuxtImg
    src="/images/hero-background.jpg"
    sizes="xs:100vw sm:100vw md:100vw lg:1200px xl:1400px"
    :modifiers="{ format: 'webp', quality: 85 }"
    alt="Hero background"
  />
</template>

Manual responsive images:

<picture>
  <source
    media="(min-width: 1200px)"
    srcset="/images/hero-1920w.webp"
    type="image/webp"
  />
  <source
    media="(min-width: 768px)"
    srcset="/images/hero-1200w.webp"
    type="image/webp"
  />
  <img
    src="/images/hero-640w.jpg"
    alt="Hero background"
    loading="lazy"
  />
</picture>

Lazy Loading

<template>
  <!-- Native lazy loading -->
  <img src="/images/product.jpg" loading="lazy" alt="Product" />

  <!-- NuxtImg with lazy loading -->
  <NuxtImg
    src="/images/product.jpg"
    loading="lazy"
    placeholder
  />
</template>

Modern Formats

Priority order:

  1. AVIF (best compression, limited support)
  2. WebP (good compression, wide support)
  3. JPEG/PNG (fallback)
<picture>
  <source srcset="/images/hero.avif" type="image/avif" />
  <source srcset="/images/hero.webp" type="image/webp" />
  <img src="/images/hero.jpg" alt="Hero" />
</picture>

Video Optimization

Compression

# H.264 for MP4 (compatibility)
ffmpeg -i input.mp4 -c:v libx264 -crf 23 -preset medium -c:a aac -b:a 128k output.mp4

# VP9 for WebM (efficiency)
ffmpeg -i input.mp4 -c:v libvpx-vp9 -crf 30 -b:v 0 output.webm

Adaptive Streaming

For longer videos, consider HLS or DASH:

# Generate HLS playlist
ffmpeg -i input.mp4 \
  -c:v libx264 -preset medium \
  -hls_time 10 -hls_playlist_type vod \
  output.m3u8

Video Attributes

<video
  preload="metadata"
  poster="/images/video-poster.jpg"
  controls
>
  <source src="/videos/demo.webm" type="video/webm" />
  <source src="/videos/demo.mp4" type="video/mp4" />
</video>

Font Optimization

Subsetting

Remove unused characters:

# Subset font to Latin characters only
pyftsubset input.ttf \
  --output-file=output.woff2 \
  --flavor=woff2 \
  --unicodes=U+0020-007F,U+00A0-00FF

Font Loading Strategies

/* Nuxt app.vue or layout */
<style>
@font-face {
  font-family: 'Inter';
  src: url('/fonts/inter-400-regular.woff2') format('woff2');
  font-weight: 400;
  font-style: normal;
  font-display: swap; /* or 'fallback' for better performance */
}
</style>

Variable Fonts

Use variable fonts to reduce requests:

@font-face {
  font-family: 'Inter Variable';
  src: url('/fonts/inter-variable.woff2') format('woff2-variations');
  font-weight: 100 900;
  font-display: swap;
}

SVG Optimization

SVGO

# Install
npm install -g svgo

# Optimize single file
svgo input.svg -o output.svg

# Batch optimize
svgo -f ./icons -o ./icons-optimized

Manual Optimization

<!-- Before: 2KB -->
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24">
  <!-- Complex paths with unnecessary precision -->
</svg>

<!-- After: 0.5KB -->
<svg viewBox="0 0 24 24" fill="none">
  <!-- Simplified paths -->
</svg>

Inline vs External

Inline for:

  • Icons used once or twice
  • Critical above-the-fold graphics
  • Styling with CSS

External for:

  • Icons used multiple times
  • Large illustrations
  • Cacheable assets

Performance Monitoring

Nuxt DevTools

<script setup>
if (process.dev) {
  // Monitor asset loading
  console.log('Asset loaded:', Date.now());
}
</script>

Lighthouse Metrics

Target metrics:

  • Largest Contentful Paint (LCP): < 2.5s
  • First Input Delay (FID): < 100ms
  • Cumulative Layout Shift (CLS): < 0.1
  • Total Blocking Time (TBT): < 300ms

Bundle Analysis

# Nuxt analyze command
npm run build -- --analyze

CDN Integration

Cloudflare Images

// nuxt.config.ts
export default defineNuxtConfig({
  image: {
    cloudflare: {
      baseURL: 'https://example.com/cdn-cgi/image/'
    }
  }
})

Usage:

<NuxtImg
  provider="cloudflare"
  src="/images/hero.jpg"
  :modifiers="{ width: 1200, quality: 85, format: 'webp' }"
/>

Custom CDN

export default defineNuxtConfig({
  app: {
    cdnURL: process.env.CDN_URL || 'https://cdn.example.com'
  }
})

Caching Strategies

Service Worker Caching

// service-worker.js
self.addEventListener('install', (event) => {
  event.waitUntil(
    caches.open('assets-v1').then((cache) => {
      return cache.addAll([
        '/images/logo.svg',
        '/fonts/inter-400-regular.woff2'
      ]);
    })
  );
});

HTTP Headers

In your deployment configuration:

# nginx.conf
location ~* \.(jpg|jpeg|png|gif|webp|svg)$ {
  expires 1y;
  add_header Cache-Control "public, immutable";
}

location ~* \.(woff|woff2|ttf|otf)$ {
  expires 1y;
  add_header Cache-Control "public, immutable";
}

Automated Optimization Pipeline

Build Script Example

// scripts/optimize-assets.js
import sharp from 'sharp';
import { glob } from 'glob';

const images = await glob('public/images/**/*.{jpg,png}');

for (const image of images) {
  // Generate WebP
  await sharp(image)
    .webp({ quality: 85 })
    .toFile(image.replace(/\.(jpg|png)$/, '.webp'));

  // Generate AVIF
  await sharp(image)
    .avif({ quality: 80 })
    .toFile(image.replace(/\.(jpg|png)$/, '.avif'));
}

Package.json Script

{
  "scripts": {
    "optimize:images": "node scripts/optimize-assets.js",
    "prebuild": "npm run optimize:images",
    "build": "nuxt build"
  }
}

Best Practices Checklist

  • Images compressed to appropriate quality
  • Multiple image formats provided (WebP, AVIF)
  • Responsive images for different viewports
  • Lazy loading for below-the-fold images
  • Fonts subsetted and using font-display
  • SVGs optimized and minified
  • Videos compressed with multiple formats
  • CDN configured for asset delivery
  • Proper caching headers set
  • Performance metrics monitored regularly

Resources

Next Steps