I inspected the QuickShrink CLI package after noticing the same image problem in several web projects: the source tree starts tidy, then screenshots, hero images, and copied assets slowly arrive in mixed formats and oversized dimensions. A browser compressor is useful for one file. A build directory needs a repeatable command.
QuickShrink CLI turns that job into a local batch step. Version 1.0.0 accepts files, directories, and glob patterns; writes JPEG, PNG, WebP, or AVIF; limits dimensions without enlarging smaller images; and strips metadata unless told otherwise. The package uses Sharp 0.34 and libvips rather than uploading files to a remote compression service.
Why the Command Line Changes the Workflow
Manual compression depends on memory. Someone has to remember to open a site, drag in each asset, choose settings, download the result, and place it in the correct directory. That can work for a single blog image, but it is easy to skip when a pull request contains dozens of files.
A CLI makes the rule executable. The same command can run on a laptop, in an npm script, or in CI. Inputs and output settings live beside the project instead of inside one developer’s browser history. That makes the result easier to repeat when another person checks out the repository.
This is also a different implementation from browser-side compression. The web version uses browser APIs and is convenient for interactive work. The CLI uses Sharp on top of libvips, so it fits folder processing and build automation. If you want the browser mechanics, my earlier breakdown of Canvas, toBlob, and image compression covers that path.
Run It Once Without a Global Install
The hosted package can run through npx, so a global install is optional:
npx https://quickshrink.orthogonal.info/cli/quickshrink.tgz \
./images \
--out ./dist/images \
--format webp \
--quality 80
The input may be a file, a directory, or a quoted glob. Directories are scanned recursively. By default, output goes into ./quickshrink-out, quality is 80, the original format is kept, and the directory structure is preserved. Use --flatten only when duplicate filenames from separate source folders cannot collide.
The package requires Node.js 18 or newer. A global install is also supported, after which the command is simply quickshrink. I prefer the explicit npx URL in a CI file because it shows exactly where the package comes from. For a long-lived project, pinning and reviewing the downloaded package before use is the safer choice.
Resize Without Accidentally Enlarging Images
Width and height limits are often more valuable than another few quality points. A 4000-pixel screenshot does not belong in a 900-pixel content column. QuickShrink exposes both limits:
quickshrink ./photos \
--out ./public/photos \
--format avif \
--quality 72 \
--max-width 1600 \
--max-height 1200
The code reads each image’s dimensions first and adds a resize only when the source exceeds a requested boundary. Sharp receives fit: "inside" and withoutEnlargement: true, so the aspect ratio stays intact and a small input is not scaled upward.
I ran a smoke test against a synthetic 1800 by 1200 PNG and requested WebP with an 800-pixel width cap. The output metadata reported 800 by 533, which confirms the proportional resize path. The file-size reduction was unusually large because the test image was a single flat color, so that number would be misleading as a photo benchmark. Real savings depend on image detail, source format, and quality.
Know What Each Encoder Setting Means
The --quality value is shared across formats, but the encoders do not interpret it identically. For JPEG, the package enables mozjpeg and passes the requested quality. PNG uses compression level 9 with palette mode. WebP and AVIF receive their own quality setting. Treat 80 as a starting point, not proof that every output will look equivalent.
For a web project, I would begin with WebP at 75 to 82, inspect text edges and gradients, then consider AVIF for large photographic assets. PNG remains useful when exact pixel values or lossless output matter. A dry run helps verify file matching before any bytes are written:
quickshrink "src/**/*.{jpg,jpeg,png}" \
--out ./public/assets \
--format webp \
--quality 78 \
--dry-run
The dry-run output lists each planned source and destination. It is especially useful with broad globs, where a typo can select more files than expected.
Metadata Is Stripped Unless You Keep It
Sharp removes EXIF and related metadata unless withMetadata() is called. QuickShrink follows that default. Add --keep-metadata when camera details, color profiles, or other embedded fields are required.
For public screenshots and web photos, removing metadata usually saves space and reduces accidental disclosure. For archival photography, evidence, or a color-managed print flow, discarding it may be the wrong choice. My byte-level EXIF GPS teardown shows why metadata deserves an explicit decision rather than a default nobody notices.
Put the Command Behind an npm Script
A named npm script gives local work and CI the same entry point:
{
"scripts": {
"images:build": "quickshrink 'src/images/**/*.{jpg,jpeg,png}' -o public/images -f webp -q 78 -w 1600",
"images:check": "quickshrink 'src/images/**/*.{jpg,jpeg,png}' -o public/images -f webp -q 78 -w 1600 --dry-run"
}
}
Run npm run images:check while adjusting a glob, then use npm run images:build in the build job. The CLI defaults concurrency to the number of CPU cores, and --concurrency can lower that value on a small runner. It processes failures per file, prints a final count, and sets a nonzero exit code when any file fails, which gives CI a useful signal.
One operational detail matters: do not write output back over the source directory. A separate output tree makes reviews clear and prevents repeated lossy encoding on later builds.
A Small Local Image Toolkit
Software is only one part of an image workflow. These are three practical categories I check when the surrounding hardware becomes the bottleneck:
- Portable USB-C SSDs for keeping originals separate from generated web assets.
- UHS-II USB-C card readers for moving larger photo batches onto a workstation.
- Monitor color calibrators for checking whether an encoder change or the display caused a visible color shift.
Full disclosure: those are affiliate links. I may earn a commission from a qualifying purchase at no extra cost to you.
Use the Browser for One-Offs, the CLI for Repetition
The web app is the faster choice when one image needs a quick size reduction and visual preview. The CLI is the better fit when the input is a directory, the settings belong in version control, or the same transformation must run on every build.
Start with the documented examples on the QuickShrink CLI page, run --dry-run, and inspect a few outputs before adding the command to CI. For more privacy-first developer tools and practical engineering notes, join Alpha Signal on Telegram.
📧 Get weekly insights on security, trading, and tech. No spam, unsubscribe anytime.
Leave a Reply