> ## Documentation Index
> Fetch the complete documentation index at: https://trigger.dev/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Process images using Sharp

> This example demonstrates how to process images using the Sharp library with Trigger.dev.

export const packages_0 = "the Sharp image processing library"

## Overview

This task processes and watermarks an image using the Sharp library, and then uploads it to R2 storage.

## Prerequisites

* A project with [Trigger.dev initialized](/quick-start)
* The [Sharp](https://sharp.pixelplumbing.com/install) library installed on your machine
* An R2-compatible object storage service, such as [Cloudflare R2](https://developers.cloudflare.com/r2)

## Adding the build configuration

To use this example, you'll first need to add these build settings to your `trigger.config.ts` file:

```ts trigger.config.ts theme={"theme":"css-variables"}
import { defineConfig } from "@trigger.dev/sdk";

export default defineConfig({
  project: "<project ref>",
  // Your other config settings...
  build: {
    // This is required to use the Sharp library
    external: ["sharp"],
  },
});
```

<Note>
  Any packages that install or build a native binary should be added to external, as native binaries
  cannot be bundled.
</Note>

## Key features

* Resizes a JPEG image to 800x800 pixels
* Adds a watermark to the image, positioned in the bottom-right corner, using a PNG image
* Uploads the processed image to R2 storage

## Task code

```ts trigger/sharp-image-processing.ts theme={"theme":"css-variables"}
import { S3Client } from "@aws-sdk/client-s3";
import { Upload } from "@aws-sdk/lib-storage";
import { logger, task } from "@trigger.dev/sdk";
import fs from "fs/promises";
import os from "os";
import path from "path";
import sharp from "sharp";

// Initialize R2 client using your R2 account details
const r2Client = new S3Client({
  region: "auto",
  endpoint: process.env.R2_ENDPOINT,
  credentials: {
    accessKeyId: process.env.R2_ACCESS_KEY_ID ?? "",
    secretAccessKey: process.env.R2_SECRET_ACCESS_KEY ?? "",
  },
});

export const sharpProcessImage = task({
  id: "sharp-process-image",
  retry: { maxAttempts: 1 },
  run: async (payload: { imageUrl: string; watermarkUrl: string }) => {
    const { imageUrl, watermarkUrl } = payload;
    const outputPath = path.join(os.tmpdir(), `output_${Date.now()}.jpg`);

    const [imageResponse, watermarkResponse] = await Promise.all([
      fetch(imageUrl),
      fetch(watermarkUrl),
    ]);
    const imageBuffer = await imageResponse.arrayBuffer();
    const watermarkBuffer = await watermarkResponse.arrayBuffer();

    await sharp(Buffer.from(imageBuffer))
      .resize(800, 800) // Resize the image to 800x800px
      .composite([
        {
          input: Buffer.from(watermarkBuffer),
          gravity: "southeast", // Position the watermark in the bottom-right corner
        },
      ])
      .jpeg() // Convert to jpeg
      .toBuffer() // Convert to buffer
      .then(async (outputBuffer) => {
        await fs.writeFile(outputPath, outputBuffer); // Write the buffer to file

        const r2Key = `processed-images/${path.basename(outputPath)}`;
        const uploadParams = {
          Bucket: process.env.R2_BUCKET,
          Key: r2Key,
          Body: await fs.readFile(outputPath),
        };

        const upload = new Upload({
          client: r2Client,
          params: uploadParams,
        });

        await upload.done();
        logger.log("Image uploaded to R2 storage.", {
          path: `/${process.env.R2_BUCKET}/${r2Key}`,
        });

        await fs.unlink(outputPath); // Clean up the temporary file
        return { r2Key };
      });
  },
});
```

## Testing your task

To test this task in the dashboard, you can use the following payload:

```json theme={"theme":"css-variables"}
{
  "imageUrl": "<an-image-url.jpg>", // Replace with a URL to a JPEG image
  "watermarkUrl": "<an-image-url.png>" // Replace with a URL to a PNG watermark image
}
```

## Local development

To test this example task locally, be sure to install any packages from the build extensions you added to your `trigger.config.ts` file to your local machine. In this case, you need to install {packages_0}.
