Adding image upload feature to your React Application
The functionality for uploading images includes some elements aimed at improving user experience. This includes user-friendly interfaces for choosing and uploading photos.
In today’s digital landscape, the ability to upload images seamlessly is a fundamental feature of numerous web and mobile applications. Take a moment to think about applications that you interact with daily. Image uploads are a major cornerstone of social media applications and e-commerce apps like Amazon to enhance user engagement.
A thoughtful and intuitive UX design with an image upload feature should provide visual feedback and image previews before uploading. This helps the user verify that they are uploading the right image. Adding progress indicators and success messages keeps the user aware and engaged during the image upload process.
How do you upload images using React? Let’s dive deep into how to upload an image, preview it, and send it to a backend server.
Step 1: Introduction
Initialize a React application using the vite tool from the command terminal. We will use the yarn package manager to install and run the scripts. Finally, navigate to the project folder and run yarn to install the dependencies.
yarn create vite@latest image-upload -- --template react-ts
Step 2: Creating a file input
This component renders a simple user interface with a file input field that allows users to select image files. The accept
attribute is set to "image/*", which specifies that the file input should only accept files with MIME type starting with "image/", effectively limiting file selection to images.
import React from "react";
import "./App.css";
function App() {
return (
<div>
<input type="file" accept="image/*" />
</div>
);
}
export default App;
Try Kodaschool for free
Click below to sign up and get access to free web, android and iOs challenges.
Step 3: Storing File in state
When a user selects a file the handleFileChange function is triggered. The image selected by the user is stored in a state variable. The selected file from the file input is retrieved by event.target.files
Finally, the setSelectedImage
function updates the selectedImage
state with the selected file.
import { FormEvent, ChangeEventHandler, useState } from "react";
import "./App.css";
function App() {
const [selectedImage, setSelectedImage] = useState<File>();
const handleFileChange: ChangeEventHandler<HTMLInputElement> = (
event
) => {
const file = event.target.files as FileList;
setSelectedImage(file?.[0]);
};
return (
<div className="wrapper">
<form>
<input type="file" onChange={handleFileChange} accept="image/*" />
</form>
</div>
);
}
export default App;
Step 4: Preview the Image
The function below converts a file to a data URL string, encapsulated within a promise to handle the file and get the image preview.
export const fileToDataString = (file: File) => {
return new Promise<string>((resolve, reject) => {
const reader = new FileReader();
reader.readAsDataURL(file);
reader.onerror = (error) => reject(error);
reader.onload = () => resolve(reader.result as string);
});
};
Once a user selects an image, the preview of the image is rendered.
. selectedImage
: Stores the selected image file by the user.
. previewImgUrl
: Stores the data URL of the selected image f
The fileToDataString
function is called with the selected file to convert it into a data URL string representing the image. Upon successful conversion, the obtained data URL is set as the value of previewImgUrl
state variable, enabling the image preview.
import { FormEvent, ChangeEventHandler, useState } from "react";
import "./App.css";
import { fileToDataString } from "./utils";
function App() {
const [selectedImage, setSelectedImage] = useState<File>();
const [previewImgUrl, setPreviewimgUrl] = useState("");
const handleFileChange: ChangeEventHandler<HTMLInputElement> = async (
event
) => {
const file = event.target.files as FileList;
setSelectedImage(file?.[0]);
if (!file) {
return;
}
try {
const imgUrl = await fileToDataString(file?.[0]);
setPreviewimgUrl(imgUrl);
} catch (error) {
console.log(error);
}
};
return (
<div className="wrapper">
{previewImgUrl && (
<div className="image_wrapper">
<img src={previewImgUrl} />
</div>
)}
<form >
<input type="file" onChange={handleFileChange} accept="image/*" />
</form>
</div>
);
}
export default App;
Step 5: Sending the Image to a server
Finally, the image uploaded from your local machine to the react app can be sent to a server. So we will now make a POST request to a REST API and pass the selectedImage
as the payload. We need to install Axios and use it to make the API request.
a) Set up Axios for React TypeScript Client
After installing Axios we set up an Axios client configured with custom headers for sending multipart/form-data.T
he Axios instance can be used throughout the application to make HTTP requests with these headers preconfigured.
import axios from "axios";
const headers: HeadersInit = {
"Content-Type": `multipart/form-data;`,
Accept: "multipart/form-data",
};
export const axiosInstance = axios.create({
baseURL: "https://localhost:5000",
headers,
});
b) Method to handle image upload to a server
The handleImageUpload function is used to handle the API request. Inside the try
block, a new FormData
object is created to store the image file selected by the user.
- If a
selectedImage
is available it is appended to theFormData
object with the key"file"
. - An Axios POST request is made to
/upload/file
endpoint using theaxiosInstance
. - The
onUploadProgress
option is provided in the request configuration to track the upload progress. Whenever progress is made, theprogressEvent
is used to calculate the percentage of completion, and theprogress
state variable is updated accordingly usingsetProgress
.
import { FormEvent,useState } from "react"
import { axiosInstance } from "./config";
const [progress, setProgress] = useState(0)
const handleImageUpload = async (event: FormEvent<HTMLFormElement>) => {
event.preventDefault();
try {
const formData = new FormData();
if (selectedImage) {
formData.append("file", selectedImage);
const response = await axiosInstance.post(`/upload/file`, formData, {
onUploadProgress: (progressEvent) => {
const progress = Math.round(
(100 * progressEvent.loaded) / progressEvent.total
);
setProgress(progress);
},
});
setProgress(0);
console.log(response);
}
} catch (error) {
console.log(error);
}
};
c) Pass the function to the form onSubmit handler
When the form is submitted, the handleImageUpload
function is called, which is responsible for sending the selected image file to the server.
<form onSubmit={handleImageUpload}>
<input type="file" onChange={handleFileChange} accept="image/*" />
<button type="submit">Upload image</button>
</form>
Congratulations!! You have done it.
Here is the complete code for the Image upload.
import { FormEvent, ChangeEventHandler, useState } from "react";
import "./App.css";
import { fileToDataString } from "./utils";
import { axiosInstance } from "./config";
function App() {
const [selectedImage, setSelectedImage] = useState<File>();
const [previewImgUrl, setPreviewimgUrl] = useState("");
const [progress, setProgress] = useState<number>(0);
const handleFileChange: ChangeEventHandler<HTMLInputElement> = async (
event
) => {
const file = event.target.files as FileList;
setSelectedImage(file?.[0]);
if (!file) {
return;
}
try {
const imgUrl = await fileToDataString(file?.[0]);
setPreviewimgUrl(imgUrl);
} catch (error) {
console.log(error);
}
};
const handleImageUpload = async (event: FormEvent<HTMLFormElement>) => {
event.preventDefault();
try {
const formData = new FormData();
if (selectedImage) {
formData.append("file", selectedImage);
const response = await axiosInstance.post(`/upload/file`, formData, {
onUploadProgress: (progressEvent) => {
const progress = Math.round(
(100 * progressEvent.loaded) / progressEvent.total
);
setProgress(progress);
},
});
setProgress(0);
console.log(response);
}
} catch (error) {
console.log(error);
}
};
return (
<div className="wrapper">
{selectedImage && progress > 0 && (
<div className="progress my-3">
<div className="progress-bar progress-bar-info"
role="progressbar">
{progress}%
</div>
</div>
)}
{previewImgUrl && (
<div className="image_wrapper">
<img src={previewImgUrl} alt="image"/>
</div>
)}
<form onSubmit={handleImageUpload}>
<input type="file" onChange={handleFileChange} accept="image/*" />
<button disabled={!selectedImage} type="submit">Upload image</button>
</form>
</div>
);
}
export default App;