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
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
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.
https://mui.com/components/buttons/#upload-button
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);
};
コメント