Uploading multiple images using react.

programming
language

In this article, we will show you how to select and upload multiple images in react.

In the sample code shown later, we will use the

  • material UI v5
  • axios
  • typescript

in the sample code I will show later.

This sample code assumes a form where comments are submitted with images.

The specification of this form is as follows

  • Text input field for comments
  • Select and upload up to 4 images
  • When you select an image, a preview of all selected images is displayed.
  • Select images with a single button
  • Selected images can be deleted.

We will also introduce some key points in the sample code.

Sample Code

The following is a sample code to upload multiple images in react.

import React, { useState } from "react";
import axios from "axios";
import TextField from "@mui/material/TextField";
import Button from "@mui/material/Button";
import CircularProgress from "@mui/material/CircularProgress";
import IconButton from "@mui/material/IconButton";
import CancelIcon from "@mui/icons-material/Cancel";

export type Props = {
	...
};

const CommentForm: React.FC<Props> = (props) => {
	const [isCommentSending, setIsCommentSending] = useState(false);
	const [images, setImages] = useState<File[]>([]);
	const maxImagesUpload = 4; // Select and upload up to 4 images
	const [commentText, setCommentText] = useState<string>("");
	const inputId = Math.random().toString(32).substring(2);

	const handleOnSubmit = async (e: React.SyntheticEvent): Promise<void> => {
		e.preventDefault();
		setIsCommentSending(true);

		const target = e.target as typeof e.target & {
			comment: { value: string };
		};

		const data = new FormData();
		images.map((image) => {
			data.append("images[]", image);
		});
		data.append("comment", target.comment?.value || "");
		const postedComment = await axios.post(
			'/api/v1/comments',
			data
		);

    setIsCommentSending(false);
	};

	const handleOnAddImage = (e: React.ChangeEvent<HTMLInputElement>) => {
		if (!e.target.files) return;
		const img: File = e.target.files[0];
		setImages([...images, img]);
	};

	const handleOnRemoveImage = (index: number) => {
    // Selected images can be deleted.
		const newImages = [...images];
		newImages.splice(index, 1);
		setImages(newImages);
	};

	return (
		<form action="" onSubmit={(e) => handleOnSubmit(e)}>
			<TextField
				name="comment"
				value={commentText}
				multiline
				minRows={1}
				maxRows={20}
				placeholder="Write a comment"
				fullWidth
				variant="standard"
				disabled={isCommentSending}
				onChange={(e) => setCommentText(e.target.value)}
			/>
      {/* Select images with a single button */}
			<label htmlFor={inputId}>
				<Button
					variant="contained"
					disabled={images.length >= maxImagesUpload}
					component="span"
				>
					Add image
				</Button>
				<input
					id={inputId}
					type="file"
					multiple
					accept="image/*,.png,.jpg,.jpeg,.gif"
					onChange={(e: React.ChangeEvent<HTMLInputElement>) =>
						handleOnAddImage(e)
					}
					style={{ display: "none" }}
				/>
			</label>
      {/* When you select an image, a preview of all selected images is displayed. */}
			{images.map((image, i) => (
				<div
					key={i}
					style={{
						position: "relative",
						width: "40%",
					}}
				>
					<IconButton
						aria-label="delete image"
						style={{
							position: "absolute",
							top: 10,
							left: 10,
							color: "#aaa",
						}}
						onClick={() => handleOnRemoveImage(i)}
					>
						<CancelIcon />
					</IconButton>
					<img
						src={URL.createObjectURL(image)}
						style={{
							width: "100%",
							borderRadius: "20px",
						}}
					/>
				</div>
			))}
			{isCommentSending ? (
				<CircularProgress />
			) : (
				<Button
					variant="contained"
					type="submit"
					disableElevation
					disabled={!commentText}
				>
					Post
				</Button>
			)}
		</form>
	);
};

export default CommentForm;

How to implement multiple image uploads in react

Here are the key points of the sample code we introduced.

The key points are as follows.

  • Using multiples in input
  • Randomly generate the id to be given to input
  • Obtaining the path to the image file selected in the form
  • Manage the selected images as an array
  • Use FormData() to send images

Using multiples in input

In the sample code, I wrote the input as follows.

<input
  id={inputId}
  type="file"
  multiple
  accept="image/*,.png,.jpg,.jpeg,.gif"
  onChange={(e: React.ChangeEvent<HTMLInputElement>) =>
    handleOnAddImage(e)
  }
  style={{ display: "none" }}
/>

By using input with maltiple, you can load multiple images with only one input and react.

Randomly generate the id to be given to input

In the component, inputId is generated as a random string, and it is given to label and input.

...
const inputId = Math.random().toString(32).substring(2);
...
<label htmlFor={inputId}>
  <Button
    variant="contained"
    disabled={images.length >= maxImagesUpload}
    component="span"
  >
    Add image
  </Button>
  <input
    id={inputId}
    type="file"
    multiple
    accept="image/*,.png,.jpg,.jpeg,.gif"
    onChange={(e: React.ChangeEvent<HTMLInputElement>) =>
      handleOnAddImage(e)
    }
    style={{ display: "none" }}
  />
</label>

By generating a random id in the component, the image will be displayed in any form even if multiple forms are set up in a page.

The way to write in the label is based on the material UI documentation.

React Button component - MUI
Buttons allow users to take actions, and make choices, with a single tap.

Obtaining the path to the image file selected in the form

The path of the image file selected in the form can be easily obtained by using URL.createObjectURL().

<img
  src={URL.createObjectURL(image)}
  style={{
    width: "100%",
    borderRadius: "20px",
  }}
/>

Manage the selected images as an array

The multiple images selected in the form are managed as an array.

  const [images, setImages] = useState<File[]>([]);
  ...

	const handleOnAddImage = (e: React.ChangeEvent<HTMLInputElement>) => {
		if (!e.target.files) return;
		const img: File = e.target.files[0];
		setImages([...images, img]);
	};

	const handleOnRemoveImage = (index: number) => {
		const newImages = [...images];
		newImages.splice(index, 1);
		setImages(newImages);
	};

  ...

When an image file is selected, it is added to the array by handleOnAddImage.

When removing the selected image from the form, the index of the array is passed to handleOnRemoveImage, and the specified image is removed from the array based on the index.

Use FormData() to send images

For sending images, you need to use FormData().

Fill the FormData() with what you want to send and post it with axios.

	const handleOnSubmit = async (e: React.SyntheticEvent): Promise<void> => {
		e.preventDefault();
		setIsCommentSending(true);

		const target = e.target as typeof e.target & {
			comment: { value: string };
		};

		const data = new FormData();
		images.map((image) => {
			data.append("images[]", image);
		});
		data.append("comment", target.comment?.value || "");
		const postedComment = await axios.post(
			'/api/v1/comments',
			data
		);

    setIsCommentSending(false);
	};
タイトルとURLをコピーしました