nuxt-image-optimizer

Nuxt module for on-demand image optimization with caching, responsive srcset/sizes, and CLS-safe placeholders
727
Install
npm install nuxt-image-optimizer
Latest Version:0.0.7
Vue:3
License:MIT
Last Updated:Oct 18, 2026
Links: GitHub  ·  npm
Maintainer: ciyoxe

Nuxt Image Optimizer

npm version npm downloads License Nuxt

Drop-in <o-img> component for Nuxt that serves resized, compressed images (WebP/AVIF/JPEG/PNG) from your own server, with no layout shift. Works with local files from public/ and with remote images from domains you allow.

Why use it

  • No build step or external service. Images are optimized on first request with Sharp and cached.
  • Fast first load. The original is served immediately while the optimized version is prepared in the background, so nobody waits for an encode.
  • No layout shift. Original image size is read during SSR and exposed as CSS variables, so the page reserves the right space.
  • Responsive. srcset/sizes with your own breakpoints, plus optional low-quality placeholders.
  • Stays fresh. Cached images are re-checked in the background and updated when the source changes.

Best suited to a single self-hosted Nuxt server. The cache lives in memory by default and is cleared on restart.

Requires Nuxt >=3.16 <5 and Node >=20.19.

Quick start

npm i nuxt-image-optimizer
// nuxt.config.ts
export default defineNuxtConfig({
  modules: ['nuxt-image-optimizer'],
  cachedImageOptimizer: {
    // remote hosts you want to optimize (local /... paths always work)
    domains: ['images.example.com'],
  },
});
<o-img src="/photo.jpg" alt="A photo" srcset="320, 640, 1024" sizes="sm:100vw, 50vw" />

Component

<o-img
  src="https://images.example.com/photo.jpg"
  alt="A photo"
  format="webp"
  quality="75"
  srcset="320, 640, 1024"
  sizes="sm:100vw, 50vw"
  placeholder="30, 320"
  preload
  @load="onLoad"
/>
Prop Description
src Image URL. /... paths are read from public/; remote hosts must be in domains. Other hosts are shown as-is, unoptimized.
format webp, avif, jpeg or png. Defaults to conversion.format.
quality 1–100. Defaults to conversion.quality.
srcset Widths to generate, e.g. "320, 640, 1024".
sizes Display width per breakpoint, e.g. "sm:100vw, 50vw". Keys come from the sizes config; a value without a key is the fallback.
placeholder Low-quality preview shown first: "quality, width, height", e.g. "30, 320". Width and height are optional.
preload Adds a preload link to the page head (use for above-the-fold images).

Events: load:placeholder (placeholder loaded), load (final image loaded), error (final image failed; placeholder errors are ignored). They also fire if the image finished loading before Vue hydrated.

Styling hooks:

  • data-status attribute: placeholder, completed or error.
  • CSS variables --original-width, --original-height, --aspect-ratio (original image, not the resized one).

Without alt, the image gets role="presentation".

How it works

  1. The component requests /api/__cimgopt with the image URL, size, format and quality.
  2. If that variant is cached, it is served. Otherwise the original is served right away and an optimization task is queued.
  3. Later requests get the optimized file. Old entries are refreshed in the background and the least-used ones are evicted when the cache is full.

Invalid or non-allowed URLs get a 400, missing images a 404, unreachable remote images a 502. Remote redirects are rejected and fetches time out after 10 seconds.

Configuration

// defaults
{
  cache: {
    // if queue is full, new optimization tasks will be rejected
    queueSize: 1000,
    // pause between optimization works to reduce CPU load, format can be like '5s', '100ms', '1m', '2h'
    queueTimeout: '5s',
    // 'in-memory' for memory cache, or storage path for filesystem cache, like '.cache/oimgs'
    storagePath: 'in-memory',
    // max cache size, format can be like '100KB', '122MB', '14GB'
    maxSize: '20MB',
    // max number of cached image sizes (only width and height caching for ssr sizes probing)
    sizeCacheMaxCount: 10000,
  },
  // background image refreshing, active only if main queue is empty
  autoRefresh: {
    // background refresh interval, cached images will be refreshed every 30 minutes by default
    // WARN: avif images processing can be very slow (up to 10 seconds for large ones) and may occur queue overflows
    maxAge: '30m',
    // if queue is full, new refresh tasks will be rejected
    queueSize: 10,
    // pause between refresh works to reduce CPU load
    queueTimeout: '5s',
  },
  conversion: {
    // 'webp' | 'avif' | 'jpeg' | 'png', overwritten by component prop
    format: 'webp',
    // 1..100, overwritten by component prop
    quality: 75,
    // default width, overwritten by component prop
    width: 4096,
    // default height, overwritten by component prop
    height: 4096,
    // codec effort, 0..10 - higher is slower but smaller file sizes
    cpuEffort: 6,
  },
  // media queries list to use in 'sizes' prop
  // you can define your own keys, like '2xl'
  sizes: {
    xs: '(width < 320px)',
    sm: '(width < 640px)',
    md: '(width < 768px)',
    lg: '(width < 1024px)',
    xl: '(width < 1280px)',
    xxl: '(width < 1536px)',
  },
  // allow‑list for remote hosts
  domains: [],
}

Development

# Install dependencies
npm install

# Develop with playground
npm run dev

# Type check
npm run test:types

# Unit tests
npm test

# Lint
npm run lint

License

MIT

Related Packages

lazy-srcset

Automatically generates placeholders, srcset, sizes, and lazy loading to reduce...

1,106 0
nuxt-proxy-request

A http proxy module for nuxt(3) powered by h3-proxy.

35,748 15
nuxt-bunny-image-statamic

Nuxt 4 module: responsive Bunny.net Optimizer images for Statamic image objects

0 0
nuxt-openapi-meta

Auto-generate Nitro defineRouteMeta (OpenAPI) for Nuxt server routes from JSDoc...

688 0
nuxt-yup

yup implementation for nuxt

2,981 4

Version History

Version Released Vue License
0.0.7 ^3.0 MIT
0.0.6 ^3.0 MIT
0.0.5 ^3.0 MIT
0.0.4 ^3.0 MIT
0.0.3 ^3.0 MIT