Image Converter

A simple image converter that allows you to convert images to different formats, optimizing them in the process. The project is built with Next.js and uses a server action to process the images.

Juan Méndez avatar
Juan Méndez📆 2025-08-13

Building an Image Converter with Next.js Server Actions

This article explains how to build a simple but powerful image converter using Next.js. The application allows users to upload images, convert them to different formats (such as AVIF, WEBP, and PNG), and download the results.

Image Converter Preview

Project Overview

The image converter is a web application where users can:

  • Drag and drop or select multiple image files.
  • See a preview of the selected images.
  • Choose the output format for the conversion.
  • Start the conversion process and see the progress.
  • Download the converted images individually or as a zip file.

The main motivation behind this project was to create a fast and easy-to-use tool for optimizing images for the web. As a web developer, I often need to convert images to modern, efficient formats like WebP or AVIF to improve website performance. This tool streamlines that process.

Core Technologies

This project is built on a selection of modern web technologies, chosen for their performance and developer experience:

  • Next.js: As a full-stack React framework, Next.js provides server-side rendering, API routes, and, most importantly for this project, Server Actions. This allows for a seamless integration of the frontend and backend in a single codebase.
  • TypeScript: Using TypeScript ensures type safety, which is crucial for a project that handles complex data structures like File objects and FormData. It helps catch errors early and makes the code more robust and maintainable.
  • Tailwind CSS: For styling, Tailwind CSS offers a utility-first approach that enables rapid development of a clean and responsive user interface without writing custom CSS.
  • Sharp: This high-performance Node.js image processing library is the core of the image converter. It's incredibly fast and supports a wide range of image formats, making it the perfect choice for this project.

The Conversion Process

The image conversion is handled by a Next.js server action. Here’s a high-level overview of the process:

  1. File Upload: The user uploads one or more images through the browser.
  2. Server Action: The files are sent to a server action.
  3. Image Processing: The server action uses the sharp library to convert each image to the desired format in memory.
  4. Download: The converted files are sent back to the client to be downloaded.

Here is a snippet of the server action responsible for the conversion:

// Core conversion logic using sharp
import sharp from "sharp";

const SHARP_FORMAT_MAP = {
  JPEG: (instance: sharp.Sharp) => instance.jpeg({ quality: 80 }),
  PNG: (instance: sharp.Sharp) => instance.png(),
  WebP: (instance: sharp.Sharp) => instance.webp({ quality: 80 }),
  AVIF: (instance: sharp.Sharp) => instance.avif({ quality: 80 }),
};

async function convertImage(
  buffer: Buffer,
  format: keyof typeof SHARP_FORMAT_MAP
) {
  const sharpInstance = sharp(buffer);
  const transformer = SHARP_FORMAT_MAP[format];

  if (!transformer) {
    throw new Error(`Unsupported format: ${format}`);
  }

  return transformer(sharpInstance).toBuffer();
}
Tip:

This approach uses a map to associate each output format with a specific Sharp transformation. This makes the code clean, extensible, and easy to maintain when adding new formats.

User Interface

The UI is built with React and Tailwind CSS, focusing on a clean and intuitive user experience. Key components include:

  • Drop Zone: For easy file selection.
  • File List: To display the selected images and their conversion status.
  • Format Selector: A dropdown to choose the output format.
  • Action Buttons: To start the conversion and download the files.

The DropZone Component

The DropZone component, built with the react-dropzone library, provides a simple way for users to select their images. It supports both drag-and-drop and the traditional file explorer.

export const DropZone = ({ setFiles }) => {
  const onDrop = useCallback((acceptedFiles: File[]) => {
    // Handle file size validation and update state
  }, [setFiles]);

  const { getRootProps, getInputProps, isDragActive } = useDropzone({ onDrop });

  return (
    <div {...getRootProps()}>
      <input {...getInputProps()} />
      <p>
        {isDragActive
          ? "Drop the files here..."
          : "Drag and drop your images here or click to select"}
      </p>
    </div>
  );
};

The FileRow Component

Once files are selected, the FileRow component is responsible for displaying each file's information, including its name, size, and the controls for conversion. It also shows the conversion status and provides a download button for each successfully converted file.

export const FileRow = ({ file, format, setFormat, onRemove, convertedFile, status }) => {
  // ... component logic
  return (
    <div className="file-row">
      <span>{file.name}</span>
      {status === "converted" && convertedFile && (
        <button onClick={handleDownload}>Download</button>
      )}
      {/* ... other controls ... */}
    </div>
  );
};

The Converter Component

The Converter component is the main component that orchestrates the entire process. It manages the state of the files, handles the conversion logic, and renders the other UI components.

export const Converter = ({ files, setFiles }) => {
  const [convertions, setConvertions] = useState([]);

  const handleConvert = async () => {
    // ... logic to call the server action
  };

  // ... other handlers and effects

  return (
    <div>
      {/* ... UI for global actions ... */}
      {convertions.map((convertion, index) => (
        <FileRow key={index} {...convertion} />
      ))}
    </div>
  );
};
Component Breakdown
Note:

The UI is designed to be responsive and works well on both desktop and mobile devices.

Conclusion

This project is a great example of how to use Next.js server actions to build a full-stack feature. It combines a modern frontend with a powerful backend to create a useful tool for image conversion.

Feel free to try out the image converter and explore the source code to learn more about the implementation.