This content originally appeared on Telerik Blogs and was authored by Thomas Findlay
A ready-made React dropzone or React upload component can make your life much easier. Let’s take a look!
There are quite a few different situations where users need to be able to upload files on a website. For instance, a social media website might allow a user to upload their profile picture, and an email client needs to enable users to attach and send
files to other people. A file can be uploaded using an input element with the type
set to file
, but let’s be honest, that’s not the best-looking way to upload files.
Fortunately, there are ready-made and feature-rich solutions available for React applications, such as React Dropzone or the React Upload component found in the KendoReact library. In this article, we will cover how to use the latter to create a nice-looking upload functionality.
Source Code
You can find the full code example for this article in this GitHub repository. Below you can also find an interactive StackBlitz example.
Project Setup
If you want to follow the article, you can create a new React project using Vite. Run the command below in your terminal.
$ npm create vite@latest how-to-work-with-the-kendoreact-upload-component
Next, we need to install the dependencies needed for the project and then start the dev server.
$ cd how-to-work-with-the-kendoreact-upload-component
$ npm install @progress/kendo-react-upload @progress/kendo-licensing @progress/kendo-theme-default
$ npm run dev
Finally, let’s do a bit of a cleanup and set up the KendoReact Default theme.
src/App.jsx
import "./App.css";
function App() {
return <div className="App"></div>;
}
export default App;
src/App.css
.App {
max-width: 40rem;
margin: 2rem auto;
}
src/main.jsx
import React from "react";
import ReactDOM from "react-dom/client";
import App from "./App";
import "@progress/kendo-theme-default/dist/all.css";
import "./index.css";
ReactDOM.createRoot(document.getElementById("root")).render(
<React.StrictMode>
<App />
</React.StrictMode>
);
That’s it for the cleanup. Let’s have a look at how we can utilize the React Upload component provided by KendoReact.
If you would like to learn more about the Kendo UI themes, check out the Building a Design System with Kendo UI article.
Adding React File Upload Component
The Upload component (also called a dropzone) provided by KendoReact is available in the @progress/kendo-react-upload
package, which we installed before. The code snippet below shows how it can be used.
src/App.jsx
import "./App.css";
import { Upload } from "@progress/kendo-react-upload";
const saveUrl = "https://demos.telerik.com/kendo-ui/service-v4/upload/save";
const removeUrl = "https://demos.telerik.com/kendo-ui/service-v4/upload/remove";
function App() {
return (
<div className="App">
<Upload
defaultFiles={[]}
withCredentials={false}
saveUrl={saveUrl}
removeUrl={removeUrl}
/>
</div>
);
}
export default App;
As the next image shows, KendoReact provides us with an elegant upload block.
And here’s the default input file element.
The difference in the UI is staggering, and that’s not all. The React Upload component supports globalization, drag-and-drop and external dropzone functionality, shows the list of uploaded files in a well-formatted list, and allows restricting minimum and maximum size, as well as allowed file types. What’s more, the upload progress is shown for each file, and new files can easily be removed.
Another nice feature is the retry ability. If an upload fails, a retry button can be used to try uploading a file again.
Let’s see how we can use some of the great features of the React Upload component by KendoReact. Next, we will see how we can disable the upload functionality and define restrictions for uploaded files, such as file types and the minimum and maximum size.
Disabling Upload and Restricting File Types and Sizes
It’s a breeze to disable and configure the size and file type restrictions in this React Upload component. To disable the Upload component, we just need to pass the disabled
prop.
src/App.jsx
<Upload
defaultFiles={[]}
withCredentials={false}
saveUrl={saveUrl}
removeUrl={removeUrl}
disabled
/>
As the image below shows, the whole Upload component will be grayed out.
The allowed file size can be restricted by passing an object with minFileSize
and maxFileSize
to the restrictions
prop.
src/App.jsx
<Upload
defaultFiles={[]}
withCredentials={false}
saveUrl={saveUrl}
removeUrl={removeUrl}
restrictions={{
minFileSize: 50000,
maxFileSize: 10000000,
}}
/>
An error is displayed if a user tries to upload a file that is smaller than minFileSize
or bigger than maxFileSize
.
File types can be restricted by providing allowedExtensions
array as part of the restrictions
prop object.
src/App.jsx
<Upload
defaultFiles={[]}
withCredentials={false}
saveUrl={saveUrl}
removeUrl={removeUrl}
restrictions={{
minFileSize: 1000,
maxFileSize: 10000000,
allowedExtensions: [".jpg", ".png"],
}}
/>
In the code snippet above, we defined that the Upload component should accept only .jpg
and .png
extensions. If we try to upload a file with a different extension, an error will be displayed, as shown in the GIF below.
Next, let’s have a look at how we can customize the list of uploaded items.
Customizing Rendered Output With a Preview
By default, the Upload component displays different icons depending on file extensions of uploaded files. But what if we would like to display a preview of the image uploaded by a user? Fortunately, this React Dropzone component lets us override the component that is used to render the uploaded files.
We can do that by passing a prop called listItemUI
. It accepts a custom component that will receive a few useful props, such as files
, disabled
, async
and methods for cancelling an upload, retrying
and removing a file. Here’s a custom UploadFileItem
component.
src/UploadFileItem.jsx
import { UploadFileStatus } from "@progress/kendo-react-upload";
import { useEffect, useState } from "react";
import { getTotalFilesSizeMessage } from "./getTotalFilesSizeMessage";
import { getFileExtensionIcon } from "./getFileExtensionIcon";
const errors = {
invalidFileExtension: "This file type is not supported.",
invalidMaxFileSize: "This file is too big.",
invalidMinFileSize: "This file is too small.",
uploadFailed: "File(s) failed to upload.",
};
const getFileError = file => {
if (file.status === UploadFileStatus.UploadFailed) {
return errors.uploadFailed;
}
if (file.validationErrors && file.validationErrors.length) {
return errors[file.validationErrors[0]];
}
return "";
};
const UploadedFileItem = props => {
const { files, onRetry, onRemove } = props;
const [preview, setPreview] = useState(null);
const file = files?.[0];
const { status } = file;
const isProgressVisible = status === UploadFileStatus.Uploading;
const isValidationError =
file.validationErrors && file.validationErrors.length;
const isUploadError = status === UploadFileStatus.UploadFailed;
const isActionVisible =
isValidationError ||
[
UploadFileStatus.Uploaded,
UploadFileStatus.Initial,
UploadFileStatus.UploadFailed,
].includes(status);
useEffect(() => {
if (![".jpg", ".jpeg", ".png"].includes(file.extension)) return;
const rawFile = file.getRawFile();
// Create a preview of an image when the upload
const reader = new FileReader();
const onLoad = e => {
console.log("on load", e);
setPreview(e.target.result);
};
reader.addEventListener("load", onLoad);
reader.readAsDataURL(rawFile);
return () => {
reader.removeEventListener("load", onLoad);
};
}, [status, file]);
return (
<>
<div className="k-file-single">
<span className="k-file-group-wrapper">
{preview ? (
<span className="k-file-group">
<img
style={{ maxWidth: "32px", display: "block" }}
src={preview}
alt={`${file.name} preview image`}
/>
</span>
) : (
<span
className={`k-file-group k-icon ${getFileExtensionIcon(file)}`}
></span>
)}
</span>
<span className="k-file-name-size-wrapper">
{isUploadError || isValidationError ? (
<>
<div className="k-file-name k-file-name-invalid">{file.name}</div>
<div className="k-file-validation-message k-text-error">
{getFileError(file)}
</div>
</>
) : (
<>
<span className="k-file-name">{file.name}</span>
<span className="k-file-size">
{getTotalFilesSizeMessage([file])}
</span>
</>
)}
</span>
</div>
<strong className="k-upload-status k-d-flex k-align-self-center">
{isProgressVisible ? (
<span className="k-upload-pct">{file.progress}%</span>
) : null}
{isUploadError ? (
<button
type="button"
className="k-button k-button-icon k-flat k-upload-action"
onClick={() => onRetry(file.uid)}
>
<span className="k-icon k-retry k-i-refresh-sm" />
</button>
) : null}
{isActionVisible ? (
<button
type="button"
className="k-button k-button-icon k-flat k-upload-action"
onClick={() => onRemove(file.uid)}
>
<span className="k-icon k-delete k-i-x" />
</button>
) : null}
</strong>
</>
);
};
export default UploadedFileItem;
The component comprises a bit of logic and markup since we have to re-implement everything for the file list item ourselves. Basically, the component renders a file’s name, an error, and retry and remove buttons. However, for a file that is
of type jpg, jpeg or png, a preview is generated using the FileReader
. If there is no preview image, one of the default Kendo UI icons is displayed.
We also need to create getTotalFilesSizeMessage
and getFileExtensionIcon
helpers. The first one calculates the total size of the image and returns the appropriate size suffix, while the latter is responsible for returning
an icon class based on the extension of a file’s extension.
src/getTotalFilesSizeMessage.js
export const getTotalFilesSizeMessage = files => {
let totalSize = 0;
let i;
if (typeof files[0].size === "number") {
for (i = 0; i < files.length; i++) {
if (files[i].size) {
totalSize += files[i].size || 0;
}
}
} else {
return "";
}
totalSize /= 1024;
if (totalSize < 1024) {
return totalSize.toFixed(2) + " KB";
} else {
return (totalSize / 1024).toFixed(2) + " MB";
}
};
src/getFileExtensionIcon.js
export const getFileExtensionIcon = file => {
switch (file.extension) {
case ".png":
case ".jpg":
case ".jpeg":
case ".tiff":
case ".bmp":
case ".gif":
return "k-i-file-image";
case ".mp3":
case ".mp4":
case ".wav":
return "k-i-file-audio";
case ".mkv":
case ".webm":
case ".flv":
case ".gifv":
case ".avi":
case ".wmv":
return "k-i-file-video";
case ".txt":
return "k-i-file-txt";
case ".pdf":
return "k-i-file-pdf";
case ".ppt":
case ".pptx":
return "k-i-file-presentation";
case ".csv":
case ".xls":
case ".xlsx":
return "k-i-file-data";
case ".html":
case ".css":
case ".js":
case ".ts":
return "k-i-file-programming";
case ".exe":
return "k-i-file-config";
case ".zip":
case ".rar":
return "k-i-file-zip";
default:
return "k-i-file";
}
};
Finally, let’s update the App
component and pass our newly created UploadFileItem
component as a prop to the React Upload component.
src/App.jsx
import "./App.css";
import { Upload } from "@progress/kendo-react-upload";
import UploadFileItem from "./UploadFileItem";
const saveUrl = "https://demos.telerik.com/kendo-ui/service-v4/upload/save";
const removeUrl = "https://demos.telerik.com/kendo-ui/service-v4/upload/remove";
function App() {
return (
<div className="App">
<Upload
defaultFiles={[]}
withCredentials={false}
saveUrl={saveUrl}
removeUrl={removeUrl}
restrictions={{
minFileSize: 1000,
maxFileSize: 10000000,
allowedExtensions: [".jpg", ".png"],
}}
listItemUI={UploadFileItem}
/>
</div>
);
}
export default App;
The GIF below shows what the Upload component looks like with our custom UploadFileItem
component.
Summary
That’s it! The React Upload component in KendoReact is a great choice for creating nice upload experiences. It has a lot of features out of the box and can be easily modified to suit your needs. What’s more, if you decide to go with KendoReact instead of the open-source alternative, React Dropzone, you also get a full UI suite of 100+ well-crafted components for anything you might need, like forms, charts, tables and more.
This content originally appeared on Telerik Blogs and was authored by Thomas Findlay
Thomas Findlay | Sciencx (2022-09-27T07:33:03+00:00) How To Implement a React Upload (DropZone) Component: KendoReact Tutorial. Retrieved from https://www.scien.cx/2022/09/27/how-to-implement-a-react-upload-dropzone-component-kendoreact-tutorial/
Please log in to upload a file.
There are no updates yet.
Click the Upload button above to add an update.