Find your landing page message
Connect a product’s value to a specific audience and write a clear first-screen message.
5,187,870 AI resources indexedWorking toward the world’s largest AI resource index.
Sharp is a powerful Node.js library for processing and transforming images, enabling resizing, format conversion, and optimization.
---
name: sharp-20806a8010ec
description: >-
Process and transform images with Sharp for Node.js. Use when a user asks to
resize images, convert image formats (WebP, AVIF, PNG, JPEG), compress images,
crop or rotate photos, generate thumbnails, add watermarks, optimize images
for web, batch process images, create responsive image variants, extract image
metadata, or build image processing pipelines. Covers resizing, format
conversion, compression, cropping, compositing, and metadata extraction.
license: Apache-2.0
compatibility: 'Node.js 20.9+ (Linux, macOS, Windows; prebuilt libvips binaries, no system install needed)'
metadata:
author: terminal-skills
version: "1.1.0"
category: content
repository: https://github.com/lovell/sharp
tags: "sharp, image, resize, thumbnail, webp"
---
# Sharp
## Overview
Sharp (0.35.x) is a high-performance image processing library for Node.js built on libvips; resizing is typically 4-5x faster than ImageMagick. It reads JPEG, PNG, WebP, AVIF, TIFF, GIF, SVG and more, and writes JPEG, PNG, WebP, AVIF, TIFF and GIF. It ships prebuilt binaries for common platforms, so `npm install sharp` is enough (the package manager must install optional dependencies). Use it for thumbnails, responsive variants, web optimization, watermarking and batch pipelines.
## Instructions
### Step 1: Installation
```bash
npm install sharp # needs Node.js >= 20.9; pnpm add sharp / yarn add sharp / bun add sharp also work
```
The examples are ES modules (`import sharp from 'sharp'`, top-level `await`), so use `.mjs` files or `"type": "module"` in `package.json`. `toFile()` resolves with an info object (`format`, `width`, `height`, `size`, ...) and rejects with an Error for a missing or corrupt input. You cannot write to the same path you read from.
### Step 2: Resize and Convert
```javascript
// resize.js — Resize images and convert between formats
import sharp from 'sharp'
// Resize to specific dimensions
await sharp('input.jpg')
.resize(800, 600) // width, height
.toFile('output.jpg')
// Resize maintaining aspect ratio (fit within bounds)
await sharp('input.jpg')
.resize(800, 600, { fit: 'inside', withoutEnlargement: true })
.toFile('resized.jpg')
// Convert JPEG to WebP
await sharp('photo.jpg')
.webp({ quality: 80 })
.toFile('photo.webp')
// Convert to AVIF (best compression, modern browsers)
await sharp('photo.jpg')
.avif({ quality: 50 }) // AVIF quality numbers are not comparable to JPEG; compare file size and look
.toFile('photo.avif')
// PNG with compression
await sharp('screenshot.png')
.png({ compressionLevel: 9, palette: true }) // palette mode for smaller files
.toFile('optimized.png')
```
### Step 3: Generate Responsive Image Variants
```javascript
// responsive.js — Generate multiple sizes for srcset
import sharp from 'sharp'
const sizes = [320, 640, 960, 1280, 1920] // common breakpoints
async function generateVariants(inputPath, outputDir) {
/**
* Generate responsive image variants in WebP and JPEG fallback.
* Args:
* inputPath: Source image path
* outputDir: Directory for output variants
*/
const metadata = await sharp(inputPath).metadata()
const baseName = inputPath.replace(/\.[^.]+$/, '').split('/').pop()
for (const width of sizes) {
if (width > metadata.width) continue // don't upscale
// WebP variant (primary)
await sharp(inputPath)
.resize(width)
.webp({ quality: 80 })
.toFile(`${outputDir}/${baseName}-${width}w.webp`)
// JPEG fallback
await sharp(inputPath)
.resize(width)
.jpeg({ quality: 80, progressive: true })
.toFile(`${outputDir}/${baseName}-${width}w.jpg`)
}
}
```
### Step 4: Crop, Rotate, and Transform
```javascript
// transform.js — Crop, rotate, flip, and extract regions
import sharp from 'sharp'
// Center crop to square (for avatars/thumbnails)
await sharp('portrait.jpg')
.resize(300, 300, { fit: 'cover', position: 'centre' })
.toFile('avatar.jpg')
// Extract specific region
await sharp('photo.jpg')
.extract({ left: 100, top: 50, width: 500, height: 500 })
.toFile('cropped.jpg')
// Rotate (auto-rotate based on EXIF, or manual)
await sharp('photo.jpg')
.rotate() // auto-rotate from EXIF orientation
.toFile('rotated.jpg')
await sharp('photo.jpg')
.rotate(90) // explicit 90° clockwise
.toFile('rotated90.jpg')
// Flip and flop
await sharp('photo.jpg').flip().toFile('flipped.jpg') // vertical flip
await sharp('photo.jpg').flop().toFile('flopped.jpg') // horizontal mirror
```
### Step 5: Watermark and Compositing
```javascript
// watermark.js — Overlay watermark, compose multiple images
import sharp from 'sharp'
// Add watermark
await sharp('photo.jpg')
.composite([{
input: 'watermark.png',
gravity: 'southeast', // bottom-right corner
blend: 'over',
}])
.toFile('watermarked.jpg')
// Text watermark (SVG text is drawn with fonts installed on the server, so
// use a generic family such as sans-serif and check the result on your host)
const svgText = `
<svg width="400" height="50">
<text x="0" y="35" font-size="30" fill="white" opacity="0.5"
font-family="sans-serif">© 2026 Northwind Studio</text>
</svg>`
await sharp('photo.jpg')
.composite([{
input: Buffer.from(svgText),
gravity: 'south',
}])
.toFile('branded.jpg')
// Create image collage (multiple images on a canvas)
await sharp({ create: { width: 1200, height: 600, channels: 3, background: '#ffffff' } })
.composite([
{ input: await sharp('img1.jpg').resize(400, 300).toBuffer(), left: 0, top: 0 },
{ input: await sharp('img2.jpg').resize(400, 300).toBuffer(), left: 400, top: 0 },
{ input: await sharp('img3.jpg').resize(400, 300).toBuffer(), left: 800, top: 0 },
])
.jpeg()
.toFile('collage.jpg')
```
### Step 6: Metadata and Analysis
```javascript
// metadata.js — Extract image metadata and stats
import sharp from 'sharp'
const metadata = await sharp('photo.jpg').metadata()
console.log(metadata)
// { width: 4032, height: 3024, format: 'jpeg', space: 'srgb',
// channels: 3, depth: 'uchar', density: 72, hasAlpha: false,
// orientation: 1, exif: Buffer, icc: Buffer }
// Get image statistics (min, max, mean for each channel)
const stats = await sharp('photo.jpg').stats()
console.log(stats.channels[0]) // { min: 0, max: 255, sum: 12345678, mean: 128.5, ... }
// Output is stripped of EXIF/ICC/XMP by default (pixels are converted to sRGB),
// so GPS and camera data are removed without extra calls. rotate() bakes in the orientation first.
await sharp('photo.jpg').rotate().toFile('stripped.jpg')
// Opt in to keeping metadata only when you need it
await sharp('photo.jpg').keepExif().toFile('with-exif.jpg') // EXIF only
await sharp('photo.jpg').keepIccProfile().toFile('with-icc.jpg') // colour profile only
await sharp('photo.jpg').keepMetadata().toFile('with-everything.jpg') // all metadata
```
### Step 7: Batch Processing
```javascript
// batch.js — Process all images in a directory
import sharp from 'sharp'
import { readdir } from 'fs/promises'
import path from 'path'
async function optimizeDirectory(inputDir, outputDir, maxWidth = 1920) {
/**
* Batch optimize all images: resize, convert to WebP, compress.
* Args:
* inputDir: Source directory with images
* outputDir: Destination for optimized images
* maxWidth: Maximum width (maintains aspect ratio)
*/
const files = await readdir(inputDir)
const imageFiles = files.filter(f => /\.(jpe?g|png|tiff?|webp)$/i.test(f))
let processed = 0
for (const file of imageFiles) {
const inputPath = path.join(inputDir, file)
const outputName = file.replace(/\.[^.]+$/, '.webp')
const outputPath = path.join(outputDir, outputName)
await sharp(inputPath)
.rotate()
.resize({ width: maxWidth, withoutEnlargement: true }) // never upscale
.webp({ quality: 80 })
.toFile(outputPath)
processed++
}
console.log(`Optimized ${processed} images`)
}
```
## Examples
### Example 1: Build an image upload pipeline for a web app
**User prompt:** "Users upload profile photos. I need to generate a 200x200 avatar thumbnail, a 800px wide display version, and a tiny 40x40 version for comments, all in WebP."
The agent will:
1. Accept the upload buffer via `sharp(buffer)`.
2. Auto-rotate based on EXIF to handle phone photos.
3. Generate three variants with `resize()` + `webp()`, using `fit: 'cover'` for the square crops.
4. Leave metadata at the default (stripped) for privacy.
5. Return buffers or save to disk/S3.
### Example 2: Optimize a website's image assets for performance
**User prompt:** "Our marketing site has 200+ images totaling 800MB. Optimize them — convert to WebP, resize anything over 1920px, and generate AVIF variants for modern browsers."
The agent will:
1. Scan the images directory, read metadata for each file.
2. Skip already-small images, resize large ones to max 1920px width.
3. Generate WebP (quality 80) and AVIF (quality 50) variants.
4. Report total size savings and per-file breakdown.
## Guidelines
- Always call `.rotate()` (no arguments) before other transforms on user uploads: it applies EXIF orientation, which phone photos depend on, and the metadata is dropped afterwards.
- Output metadata is removed by default, so uploads lose GPS coordinates and camera data unless you call `keepExif()`, `keepIccProfile()` or `keepMetadata()`. `withMetadata()` also exists but keeps the metadata instead of stripping it.
- For untrusted uploads keep the safe defaults: `failOn: 'warning'` aborts on damaged pixel data and `limitInputPixels` (about 268 million pixels) rejects decompression bombs. Do not set `unlimited: true` or `limitInputPixels: false` for user input, and validate type and size before decoding.
- Use WebP quality 80 as a default web format and AVIF for static assets: AVIF compresses better but encodes more slowly (`effort` trades speed for size). Keep a JPEG or PNG fallback for old clients.
- Pipelines are lazy and chainable; nothing runs until `toFile()`, `toBuffer()` or `toBuffer({ resolveWithObject: true })` (which also returns the output info).
- Sharp uses libuv's thread pool plus libvips threads. Under heavy concurrency, process files with a bounded pool (for example 4 at a time) instead of `Promise.all` over thousands of files, and tune `sharp.concurrency()` and `sharp.cache()` if memory is tight.
- Prefer `fit: 'cover'` for fixed-size thumbnails and `fit: 'inside'` with `withoutEnlargement: true` for max-width images.
- Animated GIF and WebP inputs are read as a single frame unless opened with `sharp(file, { animated: true })`.
---
name: sharp-20806a8010ec
description: >-
Process and transform images with Sharp for Node.js. Use when a user asks to
resize images, convert image formats (WebP, AVIF, PNG, JPEG), compress images,
crop or rotate photos, generate thumbnails, add watermarks, optimize images
for web, batch process images, create responsive image variants, extract image
metadata, or build image processing pipelines. Covers resizing, format
conversion, compression, cropping, compositing, and metadata extraction.
license: Apache-2.0
compatibility: 'Node.js 20.9+ (Linux, macOS, Windows; prebuilt libvips binaries, no system install needed)'
metadata:
author: terminal-skills
version: "1.1.0"
category: content
repository: https://github.com/lovell/sharp
tags: "sharp, image, resize, thumbnail, webp"
---
# Sharp
## Overview
Sharp (0.35.x) is a high-performance image processing library for Node.js built on libvips; resizing is typically 4-5x faster than ImageMagick. It reads JPEG, PNG, WebP, AVIF, TIFF, GIF, SVG and more, and writes JPEG, PNG, WebP, AVIF, TIFF and GIF. It ships prebuilt binaries for common platforms, so `npm install sharp` is enough (the package manager must install optional dependencies). Use it for thumbnails, responsive variants, web optimization, watermarking and batch pipelines.
## Instructions
### Step 1: Installation
```bash
npm install sharp # needs Node.js >= 20.9; pnpm add sharp / yarn add sharp / bun add sharp also work
```
The examples are ES modules (`import sharp from 'sharp'`, top-level `await`), so use `.mjs` files or `"type": "module"` in `package.json`. `toFile()` resolves with an info object (`format`, `width`, `height`, `size`, ...) and rejects with an Error for a missing or corrupt input. You cannot write to the same path you read from.
### Step 2: Resize and Convert
```javascript
// resize.js — Resize images and convert between formats
import sharp from 'sharp'
// Resize to specific dimensions
await sharp('input.jpg')
.resize(800, 600) // width, height
.toFile('output.jpg')
// Resize maintaining aspect ratio (fit within bounds)
await sharp('input.jpg')
.resize(800, 600, { fit: 'inside', withoutEnlargement: true })
.toFile('resized.jpg')
// Convert JPEG to WebP
await sharp('photo.jpg')
.webp({ quality: 80 })
.toFile('photo.webp')
// Convert to AVIF (best compression, modern browsers)
await sharp('photo.jpg')
.avif({ quality: 50 }) // AVIF quality numbers are not comparable to JPEG; compare file size and look
.toFile('photo.avif')
// PNG with compression
await sharp('screenshot.png')
.png({ compressionLevel: 9, palette: true }) // palette mode for smaller files
.toFile('optimized.png')
```
### Step 3: Generate Responsive Image Variants
```javascript
// responsive.js — Generate multiple sizes for srcset
import sharp from 'sharp'
const sizes = [320, 640, 960, 1280, 1920] // common breakpoints
async function generateVariants(inputPath, outputDir) {
/**
* Generate responsive image variants in WebP and JPEG fallback.
* Args:
* inputPath: Source image path
* outputDir: Directory for output variants
*/
const metadata = await sharp(inputPath).metadata()
const baseName = inputPath.replace(/\.[^.]+$/, '').split('/').pop()
for (const width of sizes) {
if (width > metadata.width) continue // don't upscale
// WebP variant (primary)
await sharp(inputPath)
.resize(width)
.webp({ quality: 80 })
.toFile(`${outputDir}/${baseName}-${width}w.webp`)
// JPEG fallback
await sharp(inputPath)
.resize(width)
.jpeg({ quality: 80, progressive: true })
.toFile(`${outputDir}/${baseName}-${width}w.jpg`)
}
}
```
### Step 4: Crop, Rotate, and Transform
```javascript
// transform.js — Crop, rotate, flip, and extract regions
import sharp from 'sharp'
// Center crop to square (for avatars/thumbnails)
await sharp('portrait.jpg')
.resize(300, 300, { fit: 'cover', position: 'centre' })
.toFile('avatar.jpg')
// Extract specific region
await sharp('photo.jpg')
.extract({ left: 100, top: 50, width: 500, height: 500 })
.toFile('cropped.jpg')
// Rotate (auto-rotate based on EXIF, or manual)
await sharp('photo.jpg')
.rotate() // auto-rotate from EXIF orientation
.toFile('rotated.jpg')
await sharp('photo.jpg')
.rotate(90) // explicit 90° clockwise
.toFile('rotated90.jpg')
// Flip and flop
await sharp('photo.jpg').flip().toFile('flipped.jpg') // vertical flip
await sharp('photo.jpg').flop().toFile('flopped.jpg') // horizontal mirror
```
### Step 5: Watermark and Compositing
```javascript
// watermark.js — Overlay watermark, compose multiple images
import sharp from 'sharp'
// Add watermark
await sharp('photo.jpg')
.composite([{
input: 'watermark.png',
gravity: 'southeast', // bottom-right corner
blend: 'over',
}])
.toFile('watermarked.jpg')
// Text watermark (SVG text is drawn with fonts installed on the server, so
// use a generic family such as sans-serif and check the result on your host)
const svgText = `
<svg width="400" height="50">
<text x="0" y="35" font-size="30" fill="white" opacity="0.5"
font-family="sans-serif">© 2026 Northwind Studio</text>
</svg>`
await sharp('photo.jpg')
.composite([{
input: Buffer.from(svgText),
gravity: 'south',
}])
.toFile('branded.jpg')
// Create image collage (multiple images on a canvas)
await sharp({ create: { width: 1200, height: 600, channels: 3, background: '#ffffff' } })
.composite([
{ input: await sharp('img1.jpg').resize(400, 300).toBuffer(), left: 0, top: 0 },
{ input: await sharp('img2.jpg').resize(400, 300).toBuffer(), left: 400, top: 0 },
{ input: await sharp('img3.jpg').resize(400, 300).toBuffer(), left: 800, top: 0 },
])
.jpeg()
.toFile('collage.jpg')
```
### Step 6: Metadata and Analysis
```javascript
// metadata.js — Extract image metadata and stats
import sharp from 'sharp'
const metadata = await sharp('photo.jpg').metadata()
console.log(metadata)
// { width: 4032, height: 3024, format: 'jpeg', space: 'srgb',
// channels: 3, depth: 'uchar', density: 72, hasAlpha: false,
// orientation: 1, exif: Buffer, icc: Buffer }
// Get image statistics (min, max, mean for each channel)
const stats = await sharp('photo.jpg').stats()
console.log(stats.channels[0]) // { min: 0, max: 255, sum: 12345678, mean: 128.5, ... }
// Output is stripped of EXIF/ICC/XMP by default (pixels are converted to sRGB),
// so GPS and camera data are removed without extra calls. rotate() bakes in the orientation first.
await sharp('photo.jpg').rotate().toFile('stripped.jpg')
// Opt in to keeping metadata only when you need it
await sharp('photo.jpg').keepExif().toFile('with-exif.jpg') // EXIF only
await sharp('photo.jpg').keepIccProfile().toFile('with-icc.jpg') // colour profile only
await sharp('photo.jpg').keepMetadata().toFile('with-everything.jpg') // all metadata
```
### Step 7: Batch Processing
```javascript
// batch.js — Process all images in a directory
import sharp from 'sharp'
import { readdir } from 'fs/promises'
import path from 'path'
async function optimizeDirectory(inputDir, outputDir, maxWidth = 1920) {
/**
* Batch optimize all images: resize, convert to WebP, compress.
* Args:
* inputDir: Source directory with images
* outputDir: Destination for optimized images
* maxWidth: Maximum width (maintains aspect ratio)
*/
const files = await readdir(inputDir)
const imageFiles = files.filter(f => /\.(jpe?g|png|tiff?|webp)$/i.test(f))
let processed = 0
for (const file of imageFiles) {
const inputPath = path.join(inputDir, file)
const outputName = file.replace(/\.[^.]+$/, '.webp')
const outputPath = path.join(outputDir, outputName)
await sharp(inputPath)
.rotate()
.resize({ width: maxWidth, withoutEnlargement: true }) // never upscale
.webp({ quality: 80 })
.toFile(outputPath)
processed++
}
console.log(`Optimized ${processed} images`)
}
```
## Examples
### Example 1: Build an image upload pipeline for a web app
**User prompt:** "Users upload profile photos. I need to generate a 200x200 avatar thumbnail, a 800px wide display version, and a tiny 40x40 version for comments, all in WebP."
The agent will:
1. Accept the upload buffer via `sharp(buffer)`.
2. Auto-rotate based on EXIF to handle phone photos.
3. Generate three variants with `resize()` + `webp()`, using `fit: 'cover'` for the square crops.
4. Leave metadata at the default (stripped) for privacy.
5. Return buffers or save to disk/S3.
### Example 2: Optimize a website's image assets for performance
**User prompt:** "Our marketing site has 200+ images totaling 800MB. Optimize them — convert to WebP, resize anything over 1920px, and generate AVIF variants for modern browsers."
The agent will:
1. Scan the images directory, read metadata for each file.
2. Skip already-small images, resize large ones to max 1920px width.
3. Generate WebP (quality 80) and AVIF (quality 50) variants.
4. Report total size savings and per-file breakdown.
## Guidelines
- Always call `.rotate()` (no arguments) before other transforms on user uploads: it applies EXIF orientation, which phone photos depend on, and the metadata is dropped afterwards.
- Output metadata is removed by default, so uploads lose GPS coordinates and camera data unless you call `keepExif()`, `keepIccProfile()` or `keepMetadata()`. `withMetadata()` also exists but keeps the metadata instead of stripping it.
- For untrusted uploads keep the safe defaults: `failOn: 'warning'` aborts on damaged pixel data and `limitInputPixels` (about 268 million pixels) rejects decompression bombs. Do not set `unlimited: true` or `limitInputPixels: false` for user input, and validate type and size before decoding.
- Use WebP quality 80 as a default web format and AVIF for static assets: AVIF compresses better but encodes more slowly (`effort` trades speed for size). Keep a JPEG or PNG fallback for old clients.
- Pipelines are lazy and chainable; nothing runs until `toFile()`, `toBuffer()` or `toBuffer({ resolveWithObject: true })` (which also returns the output info).
- Sharp uses libuv's thread pool plus libvips threads. Under heavy concurrency, process files with a bounded pool (for example 4 at a time) instead of `Promise.all` over thousands of files, and tune `sharp.concurrency()` and `sharp.cache()` if memory is tight.
- Prefer `fit: 'cover'` for fixed-size thumbnails and `fit: 'inside'` with `withoutEnlargement: true` for max-width images.
- Animated GIF and WebP inputs are read as a single frame unless opened with `sharp(file, { animated: true })`.
Imported from the listed AgentSkill.sh category. Complete supplied package files and notices are retained. Required external tools and credentials are not installed. No bundled code was executed during import. Individual agent task behavior has not been tested.