Skip to main content
Technology & EngineeringDesign Tool Services177 lines

Sharp

Process images at high performance with Sharp. Resize, crop, convert formats, extract metadata, and build streaming pipelines for thumbnails, responsive images, and batch transformations using libvips under the hood.

Quick Summary30 lines
You are an expert in Sharp, the high-performance Node.js image processing library powered by libvips. You build pipelines that resize, transform, and optimize images with minimal memory usage, leveraging streams and the fluent API for production workloads.

## Key Points

- **Calling `.toBuffer()` on large images in a loop** -- holds every image in memory simultaneously; use streams or process sequentially.
- **Ignoring `withoutEnlargement`** -- upscaling a 400px image to 1920px wastes bytes and produces blurry output.
- **Using JPEG for images with transparency** -- JPEG doesn't support alpha; use WebP or PNG when transparency is needed.
- **Not setting `sharp.cache(false)` in serverless** -- Sharp's internal cache grows unbounded in short-lived functions; disable it in Lambda/Cloud Functions.
- Building an image upload service that generates thumbnails and responsive variants on ingest.
- Creating an on-the-fly image resizing proxy (e.g., `?w=800&format=webp`).
- Batch-optimizing a directory of images for a static site build.
- Generating social media OG images by compositing text and graphics.
- Extracting image metadata (dimensions, EXIF, dominant color) for a media library.

## Quick Example

```bash
npm install sharp
npm install -D @types/sharp
```

```typescript
import sharp from "sharp";

// Verify installation
const info = await sharp("input.jpg").metadata();
console.log(`${info.width}x${info.height} ${info.format}`);
```
skilldb get design-tool-services-skills/sharpFull skill: 177 lines
Paste into your CLAUDE.md or agent config

Sharp Image Processing

You are an expert in Sharp, the high-performance Node.js image processing library powered by libvips. You build pipelines that resize, transform, and optimize images with minimal memory usage, leveraging streams and the fluent API for production workloads.

Core Philosophy

Pipeline, Not Procedure

Sharp operations are chained into a single pipeline that libvips executes in a streaming, memory-efficient manner. Never load an image, process it, write it, load it again for another operation. Chain everything.

Format-Aware Optimization

Each output format (WebP, AVIF, PNG, JPEG) has distinct quality/size tradeoffs. Configure format-specific options rather than using generic defaults. Serve modern formats with JPEG/PNG fallbacks.

Streams Over Buffers

For server workloads, pipe input and output streams instead of loading entire buffers into memory. This keeps memory constant regardless of image size and enables backpressure handling.

Setup

npm install sharp
npm install -D @types/sharp
import sharp from "sharp";

// Verify installation
const info = await sharp("input.jpg").metadata();
console.log(`${info.width}x${info.height} ${info.format}`);

Sharp ships prebuilt binaries for most platforms. For Alpine/Docker, use --platform=linuxmusl.

Key Patterns

Do: Chain operations in a single pipeline

await sharp("photo.jpg")
  .resize(800, 600, { fit: "cover", position: "attention" })
  .webp({ quality: 80 })
  .toFile("photo-800.webp");

Not: Create intermediate files between operations

// Wasteful -- writes and reads disk twice
await sharp("photo.jpg").resize(800, 600).toFile("temp.jpg");
await sharp("temp.jpg").webp({ quality: 80 }).toFile("photo.webp");

Do: Use streams for HTTP responses

import { createReadStream } from "node:fs";
import type { Request, Response } from "express";

function serveResized(req: Request, res: Response) {
  const width = parseInt(req.query.w as string) || 400;
  res.type("image/webp");
  createReadStream("uploads/hero.jpg")
    .pipe(sharp().resize(width).webp({ quality: 75 }))
    .pipe(res);
}

Common Patterns

Generate responsive image set

interface ImageVariant {
  width: number;
  suffix: string;
}

const variants: ImageVariant[] = [
  { width: 320, suffix: "sm" },
  { width: 768, suffix: "md" },
  { width: 1280, suffix: "lg" },
  { width: 1920, suffix: "xl" },
];

async function generateResponsiveSet(inputPath: string, outputDir: string) {
  const base = inputPath.replace(/\.[^.]+$/, "");
  await Promise.all(
    variants.map(({ width, suffix }) =>
      sharp(inputPath)
        .resize(width, null, { withoutEnlargement: true })
        .webp({ quality: 80, effort: 6 })
        .toFile(`${outputDir}/${base}-${suffix}.webp`)
    )
  );
}

Extract dominant color for placeholder

async function getDominantColor(input: string): Promise<string> {
  const { dominant } = await sharp(input).stats();
  const { r, g, b } = dominant;
  return `#${[r, g, b].map((c) => c.toString(16).padStart(2, "0")).join("")}`;
}

Composite watermark onto image

async function watermark(input: string, output: string) {
  const watermarkSvg = Buffer.from(`
    <svg width="200" height="40">
      <text x="0" y="30" font-size="28" fill="rgba(255,255,255,0.5)"
            font-family="sans-serif">PREVIEW</text>
    </svg>
  `);
  await sharp(input)
    .composite([{
      input: watermarkSvg,
      gravity: "southeast",
      blend: "over",
    }])
    .jpeg({ quality: 85 })
    .toFile(output);
}

Batch processing with concurrency control

import { readdir } from "node:fs/promises";
import { join } from "node:path";

async function batchOptimize(dir: string, concurrency = 4) {
  const files = (await readdir(dir)).filter((f) => /\.(jpe?g|png)$/i.test(f));
  const queue = [...files];

  async function worker() {
    while (queue.length) {
      const file = queue.shift()!;
      await sharp(join(dir, file))
        .resize(1920, null, { withoutEnlargement: true })
        .webp({ quality: 80 })
        .toFile(join(dir, "optimized", file.replace(/\.[^.]+$/, ".webp")));
    }
  }
  await Promise.all(Array.from({ length: concurrency }, worker));
}

Generate LQIP (Low Quality Image Placeholder)

async function generateLQIP(input: string): Promise<string> {
  const buffer = await sharp(input)
    .resize(20, null, { fit: "inside" })
    .blur(2)
    .webp({ quality: 20 })
    .toBuffer();
  return `data:image/webp;base64,${buffer.toString("base64")}`;
}

Anti-Patterns

  • Calling .toBuffer() on large images in a loop -- holds every image in memory simultaneously; use streams or process sequentially.
  • Ignoring withoutEnlargement -- upscaling a 400px image to 1920px wastes bytes and produces blurry output.
  • Using JPEG for images with transparency -- JPEG doesn't support alpha; use WebP or PNG when transparency is needed.
  • Not setting sharp.cache(false) in serverless -- Sharp's internal cache grows unbounded in short-lived functions; disable it in Lambda/Cloud Functions.

When to Use

  • Building an image upload service that generates thumbnails and responsive variants on ingest.
  • Creating an on-the-fly image resizing proxy (e.g., ?w=800&format=webp).
  • Batch-optimizing a directory of images for a static site build.
  • Generating social media OG images by compositing text and graphics.
  • Extracting image metadata (dimensions, EXIF, dominant color) for a media library.

Install this skill directly: skilldb add design-tool-services-skills

Get CLI access →

Related Skills

Storybook

Develop and test UI components in isolation with Storybook. Configure stories, addons, args, play functions, and visual testing to build a living component library with interactive documentation and automated interaction tests.

Design Tool Services164L

Tailwind Plugins

Build custom Tailwind CSS plugins that add utilities, components, and variants. Configure theme extensions, create design-system primitives, and author shareable plugins with proper type safety and documentation.

Design Tool Services196L

Canvas API

Draw graphics and generate images with the HTML Canvas 2D API and node-canvas. Render charts, create dynamic OG images, manipulate pixels, and build server-side image generation pipelines with the Canvas rendering context.

Design Tool Services219L

Chromatic

Automate visual regression testing with Chromatic. Configure snapshot capture, UI review workflows, and TurboSnap to catch unintended visual changes in Storybook components before they reach production.

Design Tool Services162L

Design Tokens

Manage cross-platform design tokens with Style Dictionary. Define token schemas, configure transforms and formats, and output CSS custom properties, Tailwind themes, iOS, and Android values from a single source of truth.

Design Tool Services201L

Figma API

Integrate with the Figma REST API and webhooks to read design files, extract components, manage variables, export images, and sync comments. Covers authentication, pagination, and real-time event handling for design-to-code workflows.

Design Tool Services166L