How to Implement Multiple Image Upload Functionality in React/Next.js

Adding multiple image upload functionality to a web application enhances user experience by allowing the association of several images with a single entity—such as a “sketch” in a design app. This guide walks you through implementing this feature in a React/Next.js frontend with a backend that stores images as an array, covering state management, file handling, UI updates, and API integration.

Step 1: Set Up the Frontend Component

Start by creating or updating a component (e.g., UploadSketchImages) to handle multiple image uploads. Use React’s useState to manage an array of image previews.

import { useState } from ‘react’;

const UploadSketchImages = () => {

 const [imagePreviews, setImagePreviews] = useState([]);

 const MAX_IMAGES = 5;

 return (

  <div>

   <input type=”file” multiple accept=”image/*” onChange={handleFileChange} />

   {/* Additional UI elements */}

  </div>

 );

};

export default UploadSketchImages;

Define an interface for image objects to track both the file and its preview URL:

interface ImagePreview {

 file: File | null;

 preview: string;

}

Step 2: Handle File Selection with an Array

Create a handleFileChange function to process multiple files into the imagePreviews array, enforcing the maximum limit.

const handleFileChange = (e: React.ChangeEvent<HTMLInputElement>) => {

 const files = e.target.files;

 if (!files) return;

 const newPreviews = Array.from(files).map((file) => ({

  file,

  preview: URL.createObjectURL(file),

 }));

 if (imagePreviews.length + newPreviews.length > MAX_IMAGES) {

  alert(`Maximum of ${MAX_IMAGES} images allowed.`);

  return;

 }

 setImagePreviews((prev) => […prev, …newPreviews]);

};

Here, Array.from(files) converts the FileList into an array, and URL.createObjectURL generates a temporary URL for previewing each image. The spread operator (…prev, …newPreviews) appends new images to the existing array without overwriting it.

Step 3: Display Images in a Tiled Layout

Render the imagePreviews array as a tiled preview, allowing users to see their selections.

<div className=”grid grid-cols-5 gap-4″>

 {imagePreviews.map((image, index) => (

  <div key={index} className=”relative w-16 h-16″>

   <img src={image.preview} alt={`Preview ${index}`} className=”w-full h-full object-cover” />

  </div>

 ))}

</div>

Use CSS (e.g., Tailwind’s grid-cols-5) to create a responsive tile layout. Each image is displayed as a thumbnail based on its preview URL.

Step 4: Add Deletion Functionality

Enable users to remove images by adding a delete button to each thumbnail, updating the array accordingly.

const removeImage = (index: number) => {

 setImagePreviews((prev) => prev.filter((_, i) => i !== index));

};

// In the render:

<div className=”grid grid-cols-5 gap-4″>

 {imagePreviews.map((image, index) => (

  <div key={index} className=”relative w-16 h-16″>

   <img src={image.preview} alt={`Preview ${index}`} className=”w-full h-full object-cover” />

   <button

    onClick={() => removeImage(index)}

    className=”absolute top-0 right-0 bg-red-500 text-white w-5 h-5 rounded-full flex items-center justify-center”

   >

    ✕

   </button>

  </div>

 ))}

</div>

const removeImage = (index: number) => {

 setImagePreviews((prev) => prev.filter((_, i) => i !== index));

};

// In the render:

<div className=”grid grid-cols-5 gap-4″>

 {imagePreviews.map((image, index) => (

  <div key={index} className=”relative w-16 h-16″>

   <img src={image.preview} alt={`Preview ${index}`} className=”w-full h-full object-cover” />

   <button

    onClick={() => removeImage(index)}

    className=”absolute top-0 right-0 bg-red-500 text-white w-5 h-5 rounded-full flex items-center justify-center”

   >

    ✕

   </button>

  </div>

 ))}

</div>

Leave a comment

Your email address will not be published. Required fields are marked *