726 lines
21 KiB
Markdown
726 lines
21 KiB
Markdown
---
|
|
name: sharp
|
|
description: Process images using the sharp Node.js library. Use when the user wants to resize, convert, crop, composite, transform, or optimize images programmatically.
|
|
---
|
|
|
|
# Sharp
|
|
|
|
API reference for sharp — a high-performance Node.js image processing library built on libvips.
|
|
|
|
Typically 4-5x faster than ImageMagick/GraphicsMagick. Supports JPEG, PNG, WebP, GIF, AVIF, TIFF, SVG, HEIF, JP2, and JXL.
|
|
|
|
Official docs: https://sharp.pixelplumbing.com
|
|
Repository: https://github.com/lovell/sharp
|
|
|
|
## Installation
|
|
|
|
```bash
|
|
npm install sharp
|
|
```
|
|
|
|
Requires Node.js ^18.17.0 or >= 20.3.0 (or Deno/Bun with Node-API v9).
|
|
|
|
## Usage
|
|
|
|
Sharp uses a fluent, chainable API. Every call returns a Sharp instance.
|
|
|
|
```js
|
|
import sharp from 'sharp';
|
|
|
|
await sharp('input.jpg')
|
|
.resize(800, 600)
|
|
.jpeg({ quality: 80 })
|
|
.toFile('output.jpg');
|
|
```
|
|
|
|
Sharp implements `stream.Duplex` — it can be piped to/from.
|
|
|
|
---
|
|
|
|
## Constructor
|
|
|
|
```js
|
|
sharp([input], [options])
|
|
```
|
|
|
|
- `input` (Buffer | string | Array): Image buffer, file path, array of inputs, or omit for stream input.
|
|
|
|
### Options
|
|
|
|
| Option | Type | Default | Description |
|
|
|--------|------|---------|-------------|
|
|
| `failOn` | string | `'warning'` | `'none'`, `'truncated'`, `'error'`, `'warning'` |
|
|
| `limitInputPixels` | number \| boolean | `268402689` | Max pixels; `false` to disable |
|
|
| `unlimited` | boolean | `false` | Remove memory safety for JPEG/PNG/SVG/HEIF |
|
|
| `autoOrient` | boolean | `false` | Auto-rotate per EXIF Orientation |
|
|
| `sequentialRead` | boolean | `true` | Sequential vs random access |
|
|
| `density` | number | `72` | DPI for vector images (1-100000) |
|
|
| `ignoreIcc` | boolean | `false` | Ignore embedded ICC profile |
|
|
| `pages` | number | `1` | Pages to extract; `-1` for all |
|
|
| `page` | number | `0` | Starting page (zero-based) |
|
|
| `animated` | boolean | `false` | Read all frames (equiv. `pages: -1`) |
|
|
|
|
### Raw Input
|
|
|
|
```js
|
|
sharp(buffer, { raw: { width: 100, height: 100, channels: 4 } })
|
|
```
|
|
|
|
| Property | Type | Description |
|
|
|----------|------|-------------|
|
|
| `width` | number | Pixel width |
|
|
| `height` | number | Pixel height |
|
|
| `channels` | number | 1-4 |
|
|
| `premultiplied` | boolean | Skip premultiplication (default `false`) |
|
|
|
|
### Create New Image
|
|
|
|
```js
|
|
sharp({ create: { width: 300, height: 200, channels: 4, background: '#ff0000' } })
|
|
```
|
|
|
|
| Property | Type | Description |
|
|
|----------|------|-------------|
|
|
| `width` | number | Pixel width |
|
|
| `height` | number | Pixel height |
|
|
| `channels` | number | 3 (RGB) or 4 (RGBA) |
|
|
| `background` | string \| Object | Color (parsed by color module) |
|
|
| `noise` | Object | `{ type: 'gaussian', mean: 128, sigma: 30 }` |
|
|
|
|
### Render Text
|
|
|
|
```js
|
|
sharp({ text: { text: 'Hello', font: 'Arial', dpi: 150 } })
|
|
```
|
|
|
|
| Property | Type | Default | Description |
|
|
|----------|------|---------|-------------|
|
|
| `text` | string | -- | UTF-8; supports Pango markup |
|
|
| `font` | string | -- | Font name |
|
|
| `fontfile` | string | -- | Absolute path to font file |
|
|
| `width` | number | `0` | Word-wrap boundary; 0 = no wrap |
|
|
| `height` | number | `0` | Max height |
|
|
| `align` | string | `'left'` | `'left'`, `'centre'`, `'center'`, `'right'` |
|
|
| `justify` | boolean | `false` | Text justification |
|
|
| `dpi` | number | `72` | Render resolution |
|
|
| `rgba` | boolean | `false` | RGBA for color emoji/Pango markup |
|
|
| `spacing` | number | `0` | Line height in points |
|
|
| `wrap` | string | `'word'` | `'word'`, `'char'`, `'word-char'`, `'none'` |
|
|
|
|
### Join Array
|
|
|
|
```js
|
|
sharp([img1, img2, img3], { join: { across: 3, shim: 10 } })
|
|
```
|
|
|
|
| Property | Type | Default | Description |
|
|
|----------|------|---------|-------------|
|
|
| `across` | number | `1` | Images per row |
|
|
| `animated` | boolean | `false` | Join as animated image |
|
|
| `shim` | number | `0` | Pixel gap between images |
|
|
| `background` | string \| Object | -- | Gap fill color |
|
|
| `halign` | string | `'left'` | `'left'`, `'centre'`, `'right'` |
|
|
| `valign` | string | `'top'` | `'top'`, `'centre'`, `'bottom'` |
|
|
|
|
### Clone
|
|
|
|
```js
|
|
const pipeline = sharp('input.jpg');
|
|
const clone1 = pipeline.clone().resize(200).toFile('thumb.jpg');
|
|
const clone2 = pipeline.clone().resize(800).toFile('large.jpg');
|
|
```
|
|
|
|
---
|
|
|
|
## Resize
|
|
|
|
```js
|
|
.resize([width], [height], [options])
|
|
```
|
|
|
|
| Option | Type | Default | Description |
|
|
|--------|------|---------|-------------|
|
|
| `width` | number | -- | Target width (null to auto-scale) |
|
|
| `height` | number | -- | Target height (null to auto-scale) |
|
|
| `fit` | string | `'cover'` | `'cover'`, `'contain'`, `'fill'`, `'inside'`, `'outside'` |
|
|
| `position` | string | `'centre'` | Gravity/position for cover/contain |
|
|
| `background` | string \| Object | `{r:0,g:0,b:0,alpha:1}` | Fill color for `contain` |
|
|
| `kernel` | string | `'lanczos3'` | `'nearest'`, `'linear'`, `'cubic'`, `'mitchell'`, `'lanczos2'`, `'lanczos3'` |
|
|
| `withoutEnlargement` | boolean | `false` | Don't upscale |
|
|
| `withoutReduction` | boolean | `false` | Don't downscale |
|
|
| `fastShrinkOnLoad` | boolean | `true` | JPEG/WebP shrink-on-load |
|
|
|
|
**Fit modes:**
|
|
- `cover` — crop to fill both dimensions
|
|
- `contain` — letterbox within dimensions
|
|
- `fill` — stretch to exact dimensions (ignores aspect ratio)
|
|
- `inside` — fit within, no exceeding
|
|
- `outside` — minimum size meeting both dimensions
|
|
|
|
**Position values:** `top`, `right top`, `right`, `right bottom`, `bottom`, `left bottom`, `left`, `left top`, `north`, `northeast`, `east`, `southeast`, `south`, `southwest`, `west`, `northwest`, `centre`/`center`
|
|
|
|
**Strategy (cover only):** `entropy`, `attention`
|
|
|
|
Only one resize per pipeline.
|
|
|
|
---
|
|
|
|
## Operations
|
|
|
|
### Rotation & Orientation
|
|
|
|
| Method | Description |
|
|
|--------|-------------|
|
|
| `.rotate([angle], [options])` | Rotate by degrees; omit angle for EXIF auto-rotate. `options.background` for fill color |
|
|
| `.autoOrient()` | Auto-orient from EXIF, then remove Orientation tag |
|
|
| `.flip([flip])` | Vertical mirror (default `true`) |
|
|
| `.flop([flop])` | Horizontal mirror (default `true`) |
|
|
|
|
### Transform
|
|
|
|
| Method | Description |
|
|
|--------|-------------|
|
|
| `.affine(matrix, [options])` | 2x2 affine transform. Options: `background`, `idx`, `idy`, `odx`, `ody`, `interpolator` |
|
|
| `.extend(extend)` | Add padding. Number for uniform, or `{ top, right, bottom, left, extendWith, background }`. `extendWith`: `'background'`, `'copy'`, `'repeat'`, `'mirror'` |
|
|
| `.extract({ left, top, width, height })` | Crop region. Can be called before and/or after resize |
|
|
| `.trim([options])` | Auto-crop to content. Options: `background` (default top-left pixel), `threshold` (default `10`), `lineArt` |
|
|
|
|
### Enhancement
|
|
|
|
| Method | Description |
|
|
|--------|-------------|
|
|
| `.sharpen([options])` | Sharpen. `options.sigma` (0.000001-10), `.m1` (flat), `.m2` (jagged), `.x1`, `.y2`, `.y3` |
|
|
| `.blur([options])` | No args: 3x3 box blur. `options.sigma` (0.3-1000) for Gaussian. Options: `precision`, `minAmplitude` |
|
|
| `.median([size])` | Median filter, default 3x3 |
|
|
| `.gamma([gamma], [gammaOut])` | Gamma correction (1.0-3.0, default 2.2) |
|
|
| `.normalise([options])` | Stretch luminance. `options.lower` (default `1`), `.upper` (default `99`) percentiles |
|
|
| `.clahe({ width, height, [maxSlope] })` | Contrast Limited Adaptive Histogram Equalization |
|
|
|
|
### Morphology
|
|
|
|
| Method | Description |
|
|
|--------|-------------|
|
|
| `.dilate([width])` | Dilation, default 1px |
|
|
| `.erode([width])` | Erosion, default 1px |
|
|
|
|
### Pixel Operations
|
|
|
|
| Method | Description |
|
|
|--------|-------------|
|
|
| `.negate([options])` | Invert colors. `options.alpha` (default `true`) |
|
|
| `.threshold([value], [options])` | Binarize at threshold (0-255, default 128). `options.greyscale` (default `true`) |
|
|
| `.boolean(operand, operator)` | Bitwise op with another image: `'and'`, `'or'`, `'eor'` |
|
|
| `.linear([a], [b])` | Per-channel linear transform: `a * pixel + b` |
|
|
| `.recomb(matrix)` | 3x3 or 4x4 color recombination matrix |
|
|
| `.modulate([options])` | Adjust `brightness` (multiply), `saturation` (multiply), `hue` (degrees), `lightness` (add) |
|
|
| `.convolve(kernel)` | Custom convolution: `{ width, height, kernel, scale, offset }` |
|
|
| `.flatten([options])` | Merge alpha with `options.background`, remove alpha |
|
|
| `.unflatten()` | Add alpha; white becomes transparent (experimental) |
|
|
|
|
---
|
|
|
|
## Colour
|
|
|
|
| Method | Description |
|
|
|--------|-------------|
|
|
| `.tint(color)` | Apply tint, preserving alpha |
|
|
| `.greyscale([bool])` | Convert to 8-bit greyscale (alias: `.grayscale()`) |
|
|
| `.pipelineColourspace(space)` | Set pipeline colorspace (e.g. `'rgb16'`, `'lab'`, `'grey16'`) |
|
|
| `.toColourspace(space)` | Set output colorspace (e.g. `'srgb'`, `'cmyk'`, `'b-w'`) |
|
|
|
|
---
|
|
|
|
## Channel
|
|
|
|
| Method | Description |
|
|
|--------|-------------|
|
|
| `.removeAlpha()` | Remove alpha channel |
|
|
| `.ensureAlpha([alpha])` | Add alpha if missing. `alpha`: 0 (transparent) to 1 (opaque, default) |
|
|
| `.extractChannel(channel)` | Extract single channel: `0`-`3` or `'red'`, `'green'`, `'blue'`, `'alpha'` |
|
|
| `.joinChannel(images, [options])` | Add channel(s) from other image(s) |
|
|
| `.bandbool(op)` | Bitwise across all bands: `'and'`, `'or'`, `'eor'` |
|
|
|
|
---
|
|
|
|
## Composite
|
|
|
|
```js
|
|
.composite(images)
|
|
```
|
|
|
|
Overlay images onto the pipeline image. `images` is an array of objects:
|
|
|
|
| Property | Type | Default | Description |
|
|
|----------|------|---------|-------------|
|
|
| `input` | Buffer \| string | -- | Image data, file path, or `create`/`text` object |
|
|
| `blend` | string | `'over'` | Blend mode |
|
|
| `gravity` | string | `'centre'` | Placement gravity |
|
|
| `top` | number | -- | Pixel offset from top (overrides gravity) |
|
|
| `left` | number | -- | Pixel offset from left (overrides gravity) |
|
|
| `tile` | boolean | `false` | Repeat overlay across image |
|
|
| `premultiplied` | boolean | `false` | Skip premultiplication |
|
|
| `density` | number | `72` | DPI for vector overlays |
|
|
|
|
**Blend modes:** `over`, `multiply`, `screen`, `overlay`, `darken`, `lighten`, `hard-light`, `soft-light`, `difference`, `exclusion`, `colour-dodge`, `colour-burn`, `add`, `saturate`, `clear`, `source`, `in`, `out`, `atop`, `dest`, `dest-over`, `dest-in`, `dest-out`, `dest-atop`, `xor`
|
|
|
|
```js
|
|
await sharp('base.png')
|
|
.composite([{ input: 'overlay.png', gravity: 'southeast' }])
|
|
.toFile('output.png');
|
|
```
|
|
|
|
---
|
|
|
|
## Output
|
|
|
|
### Write to File
|
|
|
|
```js
|
|
await sharp('input.jpg').resize(800).toFile('output.jpg');
|
|
```
|
|
|
|
Format inferred from extension. Returns `{ format, size, width, height, channels, premultiplied }`.
|
|
|
|
### Write to Buffer
|
|
|
|
```js
|
|
const buffer = await sharp('input.jpg').resize(800).toBuffer();
|
|
// or with info:
|
|
const { data, info } = await sharp('input.jpg').resize(800).toBuffer({ resolveWithObject: true });
|
|
```
|
|
|
|
### Format Methods
|
|
|
|
#### JPEG
|
|
|
|
```js
|
|
.jpeg([options])
|
|
```
|
|
|
|
| Option | Type | Default | Description |
|
|
|--------|------|---------|-------------|
|
|
| `quality` | number | `80` | 1-100 |
|
|
| `progressive` | boolean | `false` | Progressive JPEG |
|
|
| `chromaSubsampling` | string | `'4:2:0'` | `'4:2:0'` or `'4:4:4'` |
|
|
| `mozjpeg` | boolean | `false` | MozJPEG optimizations |
|
|
| `force` | boolean | `true` | Force JPEG output |
|
|
|
|
#### PNG
|
|
|
|
```js
|
|
.png([options])
|
|
```
|
|
|
|
| Option | Type | Default | Description |
|
|
|--------|------|---------|-------------|
|
|
| `progressive` | boolean | `false` | Progressive (interlace) |
|
|
| `compressionLevel` | number | `6` | 0-9 |
|
|
| `adaptiveFiltering` | boolean | `false` | Adaptive row filtering |
|
|
| `palette` | boolean | `false` | Quantise to palette |
|
|
| `quality` | number | `100` | Palette quality (1-100) |
|
|
| `effort` | number | `7` | CPU effort (1-10, palette mode) |
|
|
| `colours`/`colors` | number | `256` | Max palette colors (2-256) |
|
|
| `dither` | number | `1.0` | Floyd-Steinberg dithering level |
|
|
| `force` | boolean | `true` | Force PNG output |
|
|
|
|
#### WebP
|
|
|
|
```js
|
|
.webp([options])
|
|
```
|
|
|
|
| Option | Type | Default | Description |
|
|
|--------|------|---------|-------------|
|
|
| `quality` | number | `80` | 1-100 |
|
|
| `alphaQuality` | number | `100` | 0-100 |
|
|
| `lossless` | boolean | `false` | Lossless compression |
|
|
| `nearLossless` | boolean | `false` | Near-lossless mode |
|
|
| `smartSubsample` | boolean | `false` | Smart chroma subsampling |
|
|
| `preset` | string | `'default'` | `'default'`, `'photo'`, `'picture'`, `'drawing'`, `'icon'`, `'text'` |
|
|
| `effort` | number | `4` | 0-6 |
|
|
| `loop` | number | `0` | Animation loops (0 = infinite) |
|
|
| `delay` | number \| Array | -- | Frame delay(s) in ms |
|
|
| `force` | boolean | `true` | Force WebP output |
|
|
|
|
#### AVIF
|
|
|
|
```js
|
|
.avif([options])
|
|
```
|
|
|
|
| Option | Type | Default | Description |
|
|
|--------|------|---------|-------------|
|
|
| `quality` | number | `50` | 1-100 |
|
|
| `lossless` | boolean | `false` | Lossless mode |
|
|
| `effort` | number | `4` | 0-9 |
|
|
| `chromaSubsampling` | string | `'4:4:4'` | Chroma subsampling |
|
|
| `bitdepth` | number | `8` | 8, 10, or 12 |
|
|
|
|
#### GIF
|
|
|
|
```js
|
|
.gif([options])
|
|
```
|
|
|
|
| Option | Type | Default | Description |
|
|
|--------|------|---------|-------------|
|
|
| `reuse` | boolean | `true` | Reuse palette |
|
|
| `progressive` | boolean | `false` | Progressive (interlace) |
|
|
| `colours`/`colors` | number | `256` | 2-256 |
|
|
| `effort` | number | `7` | 1-10 |
|
|
| `dither` | number | `1.0` | 0-1 |
|
|
| `loop` | number | `0` | 0 = infinite |
|
|
| `delay` | number \| Array | -- | Frame delay(s) in ms |
|
|
| `force` | boolean | `true` | Force GIF output |
|
|
|
|
#### TIFF
|
|
|
|
```js
|
|
.tiff([options])
|
|
```
|
|
|
|
| Option | Type | Default | Description |
|
|
|--------|------|---------|-------------|
|
|
| `quality` | number | `80` | 1-100 |
|
|
| `compression` | string | `'jpeg'` | `'none'`, `'jpeg'`, `'deflate'`, `'packbits'`, `'lzw'`, `'webp'`, `'zstd'`, `'jp2k'`, `'ccittfax4'` |
|
|
| `predictor` | string | `'horizontal'` | `'none'`, `'horizontal'`, `'float'` |
|
|
| `pyramid` | boolean | `false` | Write image pyramid |
|
|
| `tile` | boolean | `false` | Tiled TIFF |
|
|
| `tileWidth` | number | `256` | Tile width |
|
|
| `tileHeight` | number | `256` | Tile height |
|
|
| `bitdepth` | number | `8` | 1, 2, 4, or 8 |
|
|
| `force` | boolean | `true` | Force TIFF output |
|
|
|
|
#### HEIF
|
|
|
|
```js
|
|
.heif({ compression: 'hevc' })
|
|
```
|
|
|
|
| Option | Type | Default | Description |
|
|
|--------|------|---------|-------------|
|
|
| `compression` | string | required | `'av1'` or `'hevc'` |
|
|
| `quality` | number | `50` | 1-100 |
|
|
| `lossless` | boolean | `false` | Lossless mode |
|
|
| `effort` | number | `4` | 0-9 |
|
|
| `bitdepth` | number | `8` | 8, 10, or 12 |
|
|
|
|
#### Raw
|
|
|
|
```js
|
|
.raw([options])
|
|
```
|
|
|
|
- `options.depth` (string, default `'uchar'`): `'char'`, `'uchar'`, `'short'`, `'ushort'`, `'int'`, `'uint'`, `'float'`, `'double'`
|
|
|
|
#### Tile (DZI / Zoomify / IIIF)
|
|
|
|
```js
|
|
.tile([options])
|
|
```
|
|
|
|
| Option | Type | Default | Description |
|
|
|--------|------|---------|-------------|
|
|
| `size` | number | `256` | Tile size (1-8192) |
|
|
| `overlap` | number | `0` | Tile overlap (0-8192) |
|
|
| `layout` | string | `'dz'` | `'dz'`, `'iiif'`, `'iiif3'`, `'zoomify'`, `'google'` |
|
|
| `container` | string | `'fs'` | `'fs'` or `'zip'` |
|
|
| `angle` | number | `0` | Rotation (multiple of 90) |
|
|
| `background` | string \| Object | white | Fill color |
|
|
|
|
---
|
|
|
|
## Metadata & Stats
|
|
|
|
### metadata()
|
|
|
|
```js
|
|
const meta = await sharp('input.jpg').metadata();
|
|
```
|
|
|
|
Returns without decoding pixels:
|
|
|
|
| Property | Type | Description |
|
|
|----------|------|-------------|
|
|
| `format` | string | `'jpeg'`, `'png'`, `'webp'`, `'gif'`, `'svg'`, etc. |
|
|
| `width` | number | Pixel width |
|
|
| `height` | number | Pixel height |
|
|
| `space` | string | Color space (`'srgb'`, `'rgb'`, `'cmyk'`, `'b-w'`, etc.) |
|
|
| `channels` | number | Band count |
|
|
| `depth` | string | Pixel depth (`'uchar'`, `'ushort'`, `'float'`, etc.) |
|
|
| `density` | number | DPI |
|
|
| `chromaSubsampling` | string | e.g. `'4:2:0'` |
|
|
| `isProgressive` | boolean | Progressive/interlaced |
|
|
| `hasAlpha` | boolean | Has alpha channel |
|
|
| `hasProfile` | boolean | Has ICC profile |
|
|
| `orientation` | number | EXIF orientation (1-8) |
|
|
| `pages` | number | Page count |
|
|
| `size` | number | Total bytes (Buffer/Stream input) |
|
|
| `exif` | Buffer | Raw EXIF |
|
|
| `icc` | Buffer | ICC profile |
|
|
| `xmp` | Buffer | XMP data |
|
|
|
|
### stats()
|
|
|
|
```js
|
|
const stats = await sharp('input.jpg').stats();
|
|
```
|
|
|
|
Returns pixel-derived statistics:
|
|
|
|
| Property | Type | Description |
|
|
|----------|------|-------------|
|
|
| `channels` | Array | Per-channel: `min`, `max`, `sum`, `mean`, `stdev`, `minX`, `minY`, `maxX`, `maxY` |
|
|
| `isOpaque` | boolean | Fully opaque |
|
|
| `entropy` | number | Greyscale entropy |
|
|
| `sharpness` | number | Laplacian sharpness |
|
|
| `dominant` | Object | Dominant sRGB color |
|
|
|
|
---
|
|
|
|
## Metadata Preservation
|
|
|
|
By default, sharp strips all metadata and converts to sRGB.
|
|
|
|
| Method | Description |
|
|
|--------|-------------|
|
|
| `.keepMetadata()` | Preserve all metadata (EXIF, ICC, XMP, IPTC) |
|
|
| `.keepExif()` | Preserve EXIF only |
|
|
| `.withExif(exif)` | Set EXIF (replaces input). Object keyed by IFD |
|
|
| `.withExifMerge(exif)` | Merge with existing EXIF |
|
|
| `.keepIccProfile()` | Preserve ICC profile |
|
|
| `.withIccProfile(icc, [options])` | Set ICC: path or `'srgb'`, `'p3'`, `'cmyk'` |
|
|
| `.keepXmp()` | Preserve XMP |
|
|
| `.withXmp(xmp)` | Set XMP (XML string) |
|
|
| `.withMetadata([options])` | Preserve most metadata. Options: `orientation`, `density` |
|
|
|
|
---
|
|
|
|
## Timeout
|
|
|
|
```js
|
|
.timeout({ seconds: 30 })
|
|
```
|
|
|
|
Abort processing after N seconds. `0` = no timeout (default).
|
|
|
|
---
|
|
|
|
## Utility (Static)
|
|
|
|
| Property/Method | Description |
|
|
|-----------------|-------------|
|
|
| `sharp.format` | Object with available input/output format booleans |
|
|
| `sharp.versions` | Version info for sharp, libvips, dependencies |
|
|
| `sharp.interpolators` | Enum: `nearest`, `bilinear`, `bicubic`, `lbb`, `nohalo`, `vsqbs` |
|
|
| `sharp.cache([options])` | Get/set cache: `{ memory: 50, files: 20, items: 100 }` |
|
|
| `sharp.concurrency([n])` | Get/set thread count (default: CPU cores) |
|
|
| `sharp.counters()` | Returns `{ queue, process }` |
|
|
| `sharp.simd([bool])` | Enable/disable SIMD (default `true`) |
|
|
| `sharp.block({ operation })` | Block specific operations |
|
|
| `sharp.unblock({ operation })` | Unblock operations |
|
|
|
|
---
|
|
|
|
## Common Recipes
|
|
|
|
### Resize and convert format
|
|
|
|
```js
|
|
await sharp('input.png')
|
|
.resize(800, 600)
|
|
.webp({ quality: 80 })
|
|
.toFile('output.webp');
|
|
```
|
|
|
|
### Resize to fit within bounds (no upscale)
|
|
|
|
```js
|
|
await sharp('input.jpg')
|
|
.resize(1200, 800, { fit: 'inside', withoutEnlargement: true })
|
|
.toFile('output.jpg');
|
|
```
|
|
|
|
### Create thumbnail (cover crop)
|
|
|
|
```js
|
|
await sharp('input.jpg')
|
|
.resize(250, 250, { fit: 'cover', position: 'attention' })
|
|
.toFile('thumb.jpg');
|
|
```
|
|
|
|
### Crop region
|
|
|
|
```js
|
|
await sharp('input.jpg')
|
|
.extract({ left: 100, top: 50, width: 400, height: 300 })
|
|
.toFile('cropped.jpg');
|
|
```
|
|
|
|
### Add watermark overlay
|
|
|
|
```js
|
|
await sharp('photo.jpg')
|
|
.composite([{ input: 'watermark.png', gravity: 'southeast' }])
|
|
.toFile('watermarked.jpg');
|
|
```
|
|
|
|
### Composite text overlay
|
|
|
|
```js
|
|
await sharp('photo.jpg')
|
|
.composite([{
|
|
input: { text: { text: 'Hello World', font: 'sans', dpi: 200, rgba: true } },
|
|
gravity: 'south'
|
|
}])
|
|
.toFile('annotated.jpg');
|
|
```
|
|
|
|
### Convert to greyscale
|
|
|
|
```js
|
|
await sharp('input.jpg')
|
|
.greyscale()
|
|
.toFile('grey.jpg');
|
|
```
|
|
|
|
### Blur
|
|
|
|
```js
|
|
await sharp('input.jpg')
|
|
.blur({ sigma: 5 })
|
|
.toFile('blurred.jpg');
|
|
```
|
|
|
|
### Rotate
|
|
|
|
```js
|
|
await sharp('input.jpg')
|
|
.rotate(90)
|
|
.toFile('rotated.jpg');
|
|
```
|
|
|
|
### Auto-orient from EXIF
|
|
|
|
```js
|
|
await sharp('input.jpg')
|
|
.autoOrient()
|
|
.toFile('oriented.jpg');
|
|
```
|
|
|
|
### Extend with padding
|
|
|
|
```js
|
|
await sharp('input.png')
|
|
.extend({ top: 20, bottom: 20, left: 20, right: 20, background: '#ffffff' })
|
|
.toFile('padded.png');
|
|
```
|
|
|
|
### Auto-trim whitespace
|
|
|
|
```js
|
|
await sharp('input.png')
|
|
.trim({ threshold: 10 })
|
|
.toFile('trimmed.png');
|
|
```
|
|
|
|
### Optimize JPEG for web
|
|
|
|
```js
|
|
await sharp('input.jpg')
|
|
.resize(1920, null, { withoutEnlargement: true })
|
|
.jpeg({ quality: 75, mozjpeg: true, progressive: true })
|
|
.toFile('optimized.jpg');
|
|
```
|
|
|
|
### Generate AVIF from JPEG
|
|
|
|
```js
|
|
await sharp('input.jpg')
|
|
.avif({ quality: 50, effort: 4 })
|
|
.toFile('output.avif');
|
|
```
|
|
|
|
### Extract channel
|
|
|
|
```js
|
|
await sharp('input.png')
|
|
.extractChannel('red')
|
|
.toFile('red-channel.png');
|
|
```
|
|
|
|
### Get image metadata
|
|
|
|
```js
|
|
const { width, height, format, space } = await sharp('input.jpg').metadata();
|
|
```
|
|
|
|
### Buffer round-trip
|
|
|
|
```js
|
|
const buffer = await sharp('input.jpg')
|
|
.resize(300)
|
|
.png()
|
|
.toBuffer();
|
|
```
|
|
|
|
### Create solid color image
|
|
|
|
```js
|
|
await sharp({ create: { width: 100, height: 100, channels: 4, background: '#ff6600' } })
|
|
.png()
|
|
.toFile('orange.png');
|
|
```
|
|
|
|
### Join images into grid
|
|
|
|
```js
|
|
await sharp(['a.png', 'b.png', 'c.png', 'd.png'], { join: { across: 2 } })
|
|
.toFile('grid.png');
|
|
```
|
|
|
|
### Preserve metadata
|
|
|
|
```js
|
|
await sharp('input.jpg')
|
|
.resize(800)
|
|
.keepMetadata()
|
|
.toFile('output.jpg');
|
|
```
|
|
|
|
### Animated GIF resize
|
|
|
|
```js
|
|
await sharp('input.gif', { animated: true })
|
|
.resize(200)
|
|
.gif()
|
|
.toFile('small.gif');
|
|
```
|
|
|
|
### Multiple outputs from one input
|
|
|
|
```js
|
|
const pipeline = sharp('input.jpg');
|
|
await Promise.all([
|
|
pipeline.clone().resize(200).toFile('thumb.jpg'),
|
|
pipeline.clone().resize(800).toFile('medium.jpg'),
|
|
pipeline.clone().resize(1600).toFile('large.jpg'),
|
|
]);
|
|
```
|
|
|
|
---
|
|
|
|
## Source
|
|
|
|
- Repository: https://github.com/lovell/sharp
|
|
- Documentation: https://sharp.pixelplumbing.com
|
|
- API — Constructor: https://sharp.pixelplumbing.com/api-constructor
|
|
- API — Input: https://sharp.pixelplumbing.com/api-input
|
|
- API — Resize: https://sharp.pixelplumbing.com/api-resize
|
|
- API — Operations: https://sharp.pixelplumbing.com/api-operation
|
|
- API — Colour: https://sharp.pixelplumbing.com/api-colour
|
|
- API — Channel: https://sharp.pixelplumbing.com/api-channel
|
|
- API — Composite: https://sharp.pixelplumbing.com/api-composite
|
|
- API — Output: https://sharp.pixelplumbing.com/api-output
|
|
- API — Utility: https://sharp.pixelplumbing.com/api-utility
|