assetopt

Guide

assetopt vs imagemin

assetopt and imagemin compared: one CLI for images, CSS, JS and SVG with smart WebP/AVIF routing, incremental cache and a CI gate — versus a plugin-based Node image library. Honest, up to date, MIT.

imagemin is the long-standing Node library for image optimization: a small core plus a plugin per codec (imagemin-mozjpeg, imagemin-pngquant, imagemin-webp, imagemin-svgo…). assetopt is a CLI that optimizes images and CSS, JS and SVG in one command, with format routing, an incremental cache and a CI quality gate built in.

Both are open source and run locally with no upload. This page lays out where each one fits — including when imagemin is the better call.

TL;DR

assetopt imagemin
Shape CLI (assetopt optimize ./dir) Library + optional imagemin-cli
Asset types Images, CSS, JS, SVG Images, SVG only
Image engine sharp (libvips) one plugin per codec
Format conversion Built-in presets + formatMatrix Add a plugin (imagemin-webp, …)
Transparency-aware routing Yes (PNG → AVIF/WebP by alpha) No
Incremental cache Yes, on by default No
CI quality gate Yes (--min-savings, exit code) No (build it yourself)
Config one .assetoptrc, walk-up JS config / plugin flags
Setup one package core + N plugins
License MIT MIT

Where they differ

Scope: images-only vs every static asset

imagemin is, by design, about raster images and SVG. If your build also ships CSS and JS, you reach for other tools (cssnano, terser/esbuild) and wire them separately.

assetopt processes four asset families in a single pass, one well-known library per type:

  • Imagessharp (JPEG, PNG, WebP, AVIF)
  • CSSlightningcss (Rust)
  • JSesbuild (Go)
  • SVGsvgo
assetopt optimize ./public   # images + css + js + svg, one command

Configuration: one file vs a plugin graph

With imagemin you compose the pipeline yourself — install a plugin per format and pass options to each:

// imagemin: you assemble the pipeline
import imagemin from 'imagemin';
import imageminMozjpeg from 'imagemin-mozjpeg';
import imageminWebp from 'imagemin-webp';

await imagemin(['images/*.{jpg,png}'], {
  destination: 'build/images',
  plugins: [imageminMozjpeg({ quality: 82 }), imageminWebp({ quality: 82 })],
});

assetopt ships sensible defaults and presets, so the equivalent is one line of config or none at all:

{ "preset": "web-perf" }

web-perf routes each source format to its lightest modern equivalent (JPEG → WebP, opaque PNG → WebP, transparent PNG → AVIF) with no per-plugin wiring. You can still drop to a per-format formatMatrix when you want fine control.

Smart, content-aware format routing

imagemin converts what you tell it to convert: add imagemin-webp and everything eligible becomes WebP. It doesn’t look at image content to decide.

assetopt’s web-perf preset is transparency-aware — a PNG with no alpha channel goes to WebP (lighter than AVIF on opaque images), a PNG with alpha goes to AVIF (keeps transparency at a smaller size). That decision is taken from sharp metadata in a single pass, nothing to configure.

Re-runs: incremental cache vs full reprocessing

imagemin reprocesses every matched file on every run. assetopt keeps a manifest (.assetopt-cache.json) keyed on sha256(source ⊕ config ⊕ core version), so a second run only touches new or changed files:

assetopt optimize ./public   # 1st run — everything processed
assetopt optimize ./public   # 2nd run — unchanged files marked (cached) ✓

The cache invalidates automatically when a source file, the config, or the core version changes.

CI: a built-in quality gate

imagemin gives you an optimizer; enforcing a savings floor in CI is on you. assetopt has a flag for it:

assetopt optimize dist --min-savings 15   # exit 1 if total savings < 15%

There’s also assetopt audit to flag oversized assets (exit code 1 in CI) without writing anything.

When imagemin is the better pick

Being honest matters more than winning every row:

  • You need a programmatic library, not a CLI. imagemin is built to be embedded in a custom Node script or gulp/webpack pipeline. assetopt’s headline surface is the CLI (a @assetopt/core API exists, but imagemin’s plugin model is more granular for bespoke pipelines).
  • You depend on a specific codec plugin (e.g. a particular pngquant quantization, or a niche format) that imagemin’s ecosystem already covers.
  • You only touch images and already have CSS/JS handled elsewhere and don’t want to move that.

Trying assetopt

npm install -g @assetopt/cli
assetopt analyze ./public        # dry-run: see savings, writes nothing
assetopt optimize ./public

analyze reports the exact before/after per file without writing anything, so you can compare it against your current imagemin output before switching. See the feature catalog for the full picture, or the convert to WebP guide for a focused walkthrough.

Get the CLI

$ npm install -g @assetopt/cli

Open source, MIT. See the docs orstar it on GitHub.