mrkeyoor.com_
Wed 05 Aug 05:00 UTC
npmUtilsupdated 05 Aug 2026

sharp

sharp is the standard image processing library for Node.js. It wraps libvips, a C library, behind a chainable JavaScript API: load an image from a file, Buffer, or stream, then resize, crop, rotate, composite, and re-encode it as JPEG, PNG, WebP, GIF, or AVIF. The README claims resizing is typically 4x-5x faster than the fastest ImageMagick settings, and in practice it is the piece that powers most Node image pipelines, including Next.js image optimization. It works on Node 20.9+, Deno, and Bun via Node-API v9.

Verdict

If you process images in Node and can tolerate native binaries, install sharp and stop looking; nothing else in the ecosystem is close on speed or correctness. The only real costs are deployment friction on mismatched platforms and a 0.x version scheme that makes minor bumps worth reading the changelog for.

API stability4/5The chainable core API has been recognizable for years, but sharp is still versioned 0.x, so breaking changes land in minor releases and upgrades deserve a changelog read.
Docs5/5Dedicated docs site (sharp.pixelplumbing.com) with full API reference, installation guide covering the hairy platform cases, published benchmarks, and a changelog.
Maintenance5/5Actively maintained by Lovell Fuller with steady releases (0.35.3 published July 2026, repo pushed July 2026) and a low open-issue count (117) for its install-support surface.
Ecosystem5/5Around 83M weekly downloads and the default image layer for major frameworks including Next.js image optimization; it is the assumed answer for Node image work.

Use it if

  • You generate thumbnails, responsive image sets, or WebP/AVIF variants server-side and care about throughput
  • You need correct handling of EXIF orientation, ICC color profiles, and alpha channels without doing it by hand
  • You process user uploads and want stream or Buffer pipelines instead of writing temp files
  • You are on Node 20.9+, Deno, or Bun and can ship platform-specific native binaries
Skip it if

Setup reality

npm install sharp usually just works on mainstream macOS, Windows, and Linux because prebuilt binaries exist for those platforms. The pain starts at the edges: cross-platform installs (developing on macOS, deploying to Linux Lambda or Alpine) need explicit --os, --cpu, and sometimes --libc install flags or optionalDependencies overrides; pnpm needs sharp allow-listed for postinstall scripts in some setups; and corporate proxies that block GitHub release downloads used to be a classic failure mode. Requires Node 20.9+, which rules out older LTS images. Budget an hour the first time you containerize it.

Patterns

Resize to a target width, keeping aspect ratioresize-image

import sharp from 'sharp';

await sharp('input.jpg')
  .resize({ width: 800 })
  .toFile('output.jpg');

Omit height and sharp preserves aspect ratio; by default small images ARE enlarged, pass withoutEnlargement: true to prevent upscaling.

Convert any input to WebP with a quality settingconvert-to-webp

const webp = await sharp('input.png')
  .webp({ quality: 80 })
  .toBuffer();

For animated GIF/WebP input you must open with sharp(input, { animated: true }) or you only get the first frame.

Center-cropped square thumbnailsquare-thumbnail

await sharp('input.jpg')
  .resize(300, 300, { fit: 'cover', position: 'attention' })
  .toFile('thumb.jpg');

position: 'attention' crops toward the region with highest detail; drop it to get a plain center crop.

Respect EXIF orientation before resizingauto-orient

await sharp('camera-photo.jpg')
  .autoOrient()
  .resize({ width: 1200 })
  .toFile('web.jpg');

Without autoOrient(), phone photos come out sideways because EXIF rotation is metadata, not pixels; output EXIF orientation is reset for you.

Read dimensions and format without decoding pixelsread-metadata

const { width, height, format, hasAlpha } = await sharp('input.jpg').metadata();
console.log(width, height, format, hasAlpha);

metadata() reads the header only, so it is cheap; width/height are pre-rotation values unless you check the orientation field.

Overlay a watermark in a cornercomposite-watermark

await sharp('photo.jpg')
  .composite([{ input: 'logo.png', gravity: 'southeast' }])
  .toFile('watermarked.jpg');

composite() runs after resize in the pipeline, so the overlay is placed on the resized canvas; oversize overlays throw.

Process an upload stream without temp filesstream-pipeline

import { pipeline } from 'node:stream/promises';

const transformer = sharp().resize({ width: 1024 }).jpeg({ mozjpeg: true });
await pipeline(request, transformer, fs.createWriteStream('out.jpg'));

sharp() with no input argument returns a duplex stream; errors surface on the pipeline promise, not the response.

Get the output Buffer plus final dimensionsbuffer-with-info

const { data, info } = await sharp(inputBuffer)
  .resize({ width: 640 })
  .png()
  .toBuffer({ resolveWithObject: true });
console.log(info.width, info.height, info.size);

info reflects the actual output after resize, useful when fit: 'inside' produced dimensions you did not specify.

Create a solid-color image from scratchgenerate-image

const png = await sharp({
  create: {
    width: 48,
    height: 48,
    channels: 4,
    background: { r: 255, g: 0, b: 0, alpha: 0.5 }
  }
}).png().toBuffer();

You must call an output format method like .png(); raw created pixels have no default encoding.

Round corners using an SVG maskrounded-corners

const mask = Buffer.from(
  '<svg><rect x="0" y="0" width="200" height="200" rx="50" ry="50"/></svg>'
);
await sharp('input.jpg')
  .resize(200, 200)
  .composite([{ input: mask, blend: 'dest-in' }])
  .png()
  .toFile('rounded.png');

Output must be a format with alpha (PNG/WebP/AVIF); JPEG output flattens the transparency to black.

Smaller JPEGs with mozjpeg settingsoptimize-jpeg

await sharp('input.jpg')
  .jpeg({ mozjpeg: true, quality: 78 })
  .toFile('optimized.jpg');

mozjpeg: true trades encode speed for smaller files; it is a preset of options, not a different encoder binary.

Encode AVIF for modern browsersconvert-to-avif

await sharp('input.jpg')
  .avif({ quality: 50, effort: 4 })
  .toFile('output.avif');

AVIF encoding is much slower than WebP at higher effort values; quality numbers are not comparable across formats.

Alternatives

PackageRegistryPick it when
jimpnpmPure JavaScript, zero native dependencies: prefer it when install reliability matters more than speed
canvasnpmYou need to draw (text, shapes, charts) onto images rather than transform photos
pillowPyPIYour image pipeline lives in Python, not Node