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:
- Images —
sharp(JPEG, PNG, WebP, AVIF) - CSS —
lightningcss(Rust) - JS —
esbuild(Go) - SVG —
svgo
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/coreAPI exists, but imagemin’s plugin model is more granular for bespoke pipelines). - You depend on a specific codec plugin (e.g. a particular
pngquantquantization, 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.