Let's Create a Drag & Drop Image Uploader on React 01

List Of Content

ADS Area (CARBON)

ADS Area (CARBON)

 

Getting Started

In this is Tutorial we will try to create a full working Image Uploader with a Drag and Drop feature on React on the Series of learning React from the ground up.

So if you are new around here or new to react in general and want to start from the basics Here are the React tutorials with Practical Applications and Examples you can easily learn from.

Also, clone the React-Examples if you weren't following along the series so you could go right to the point with the boilerplate project.

For following along the Working Convention, create a new Folder ImageUploader/ and create a renderer.js on it so we could render the ImageUploader components.

We will use the Blueprintjs Framework for a Ready-Made Components so make sure to install it on your project workspace.

npm install @blueprintjs/core @blueprintjs/icons 

Check it on the Official Web page.

Also, you can Grab the Full Source Code of this tutorial on Github, make sure to access the ImageUploader Branch.

Custom Style

We are going to use a custom style sheet for the making the image uploader which will allow me to explain what is it and how it works rather than focusing the CSS which is, in this case, is optional, so just copy the below SASS style and put it on a new file name it style.scss in the working directory ImageUploader/.

//Blueprint Style 
@import "../node_modules/@blueprintjs/core/lib/css/blueprint.css";
@import "../node_modules/@blueprintjs/icons/lib/css/blueprint-icons.css";
.inner-container {
    padding: 12px;
    display: flex;
    justify-content: center;
    align-items: center;
    .sub-header {
        text-align: center;
        font-size: 18px;
        margin-bottom: 7px;
    }
    .draggable-container {
        min-width: 40em;
        max-width: 45em;
        min-height: 20em;
        max-height: 18em;
        background-color: rgba(147, 148, 148, 0.23);
        padding: 6px;
        border-radius: 4px;
        border: 2px dashed rgba(95, 92, 92, 0.2);
        font-size: 14px;
        display: flex;
        margin-bottom: 7px;
        position: relative;
        overflow: hidden;
        #file-browser-input {
            width: 100%;
            height: 100%;
            top: 0px;
            left: 0px;
            position: absolute;
            color: transparent;
            opacity: 0;
        }
        .files-preview-container {
            display: flex;
            width: 100%;
            height: fit-content;
            flex-direction: row;
            flex-wrap: nowrap;
            z-index: 99;
            overflow-x: auto;
            overflow-y: hidden;
            flex-shrink: 0;
            padding: 5px;
            .file {
                width: 5em;
                height: 58px;
                background-color: rgba(101, 97, 97, 0.34);
                position: relative;
                margin-left: 8px;
                border: 1px solid #fff;
                border-radius: 4px;
                box-shadow: 0px 0px 14px 3px rgba(15, 15, 15, 0.2);
                flex-shrink: 0;
                img {
                    width: 100%;
                    height: 100%;
                }
                .container {
                    display: flex;
                    position: absolute;
                    flex-direction: column;
                    top: 0;
                    left: 0;
                    width: 100%;
                    height: 100%;
                    padding: 0;
                    transition: background, 160ms ease-in-out;
                    &:hover {
                        background-color: rgba(154, 151, 151, 0.29);
                        .remove-btn {
                            visibility: visible;
                        }
                    }
                    .remove-btn {
                        color: #ca4240;
                        width: fit-content;
                        position: absolute;
                        right: -7px;
                        top: -7px;
                        z-index: 99;
                        cursor: pointer;
                        visibility: hidden;
                    }
                    .progress-bar {
                        position: absolute;
                        width: 53px;
                        top: 50%;
                        transform: translateY(-50%);
                        display: flex;
                        align-self: center;
                    }
                }
            }
        }
        .helper-text {
            position: absolute;
            display: flex;
            justify-self: center;
            align-self: center;
            left: 50%;
            transform: translateX(-50%);
            color: #797676;
        }
        .file-browser-container {
            position: absolute;
            display: flex;
            justify-self: center;
            align-self: center;
            bottom: 8px;
            left: 50%;
            transform: translateX(-50%);
        }
    }
}

.footer-container {
    width: 100%;
    display: flex;
    flex-direction: row;
    justify-content: center;
    align-items: center;
}

Image Uploader Component

The is going to be the main component that takes care of reading images and handling dropped images, create a file draggableUploader.jsx and make sure to name the extensions as (.jsx) cause it will ensure whatever Code Editor or IDE (VSCode) that this is using React special syntax.

import React from "react";

import {AnchorButton, Intent} from "@blueprintjs/core";

export default class DraggableUploader extends React.Component {

  constructor(props) {
    super(props);
    this.state = {};
  }

  render() {
    return (
      <div
        className="inner-container"
        style={{
        display: "flex",
        flexDirection: "column"
      }}>
        <div className="sub-header">Drag an Image</div>
        <div className="draggable-container">
          <input
            type="file"
            id="file-browser-input"
            name="file-browser-input"
            ref={input => this.fileInput = input}/>
          <div className="files-preview-container"></div>
          <div className="helper-text">Drag and Drop Images Here</div>
          <div className="file-browser-container">
            <AnchorButton
              text="Browse"
              intent={Intent.PRIMARY}
              minimal={true}
              onClick={() => this.fileInput.click()}/>
          </div>
        </div>
        <AnchorButton text="Upload" intent={Intent.SUCCESS}/>
      </div>
    );
  }
}

The above structure may look a bit complicated but, in reality, it is super easy to understand, let me walk you through the elements we are trying to render.

the Subheader is for showing a minimal header on the centered on the top of the draggable container.

Then we simply create a container to hold the draggable container which is basically the area you drag and drop images on or you see previews of already loaded images, we create an input but it's not going to show since we don't want to display an input bar but instead we have to create it and hide behind the scenes using the power of CSS.

we Also have a preview container for the loaded images which we will cover in the next tutorial alongside a helper text centered on the draggable container for showing some hint, and finally, the browse button which basically triggers the click event on the fileInput that we created.

If you can tell that we are using some components from the blueprints framework which is an awesome framework for Web Developer (more specifically React Devs). 

Now let's add the Drag & Drop feature for the image uploader.

...
<input
type="file"
id="file-browser-input"
name="file-browser-input"
ref={input => this.fileInput = input}
onDragOver={(e) => {
e.preventDefault();
   e.stopPropagation();
}}
onDrop={this
.onFileLoad
.bind(this)}
onChange={this
.onFileLoad
.bind(this)}/>
...

The Drag & Drop has to be added to the input element since we are giving a full width and height of the draggable-container so whenever you drop an image the event will be triggered on the hidden input.

onDragOver we prevent the default behavior of the input so we would be able to capture the dropped file also we stop propagation.

onDrop & onChange will take care of loading and reading the image from the host machine, so now we have to add the appropriate method for it.

onFileLoad(e) {
  //Get current selected or dropped file (it gives you the ability to load multiple images).
  const file = e.currentTarget.files[0];
  //Create instance 
  let fileReader = new FileReader();
  //Register event listeners
  fileReader.onload = () => {
    console.log("IMAGE LOADED: ", fileReader.result);
  }
  //Operation Aborted 
  fileReader.onabort = () => {
    alert("Reading Aborted");
  }
  //Error when loading 
  fileReader.onerror = () => {
    alert("Reading ERROR!");
  }
  //Read the file as a Data URL (which gonna give you a base64 encoded image data)
  fileReader.readAsDataURL(file);
}

The Above method simply gets the Dropped or Selected image since we are registering the onFileLoad callback on both events (onDrop & onChange) then we use the FileReader to read it as a Base64 encoded pixels data.

Rendering Components

Now the time of truth we have to render everything to the DOM into the root container we created on the index.html, so on the imageUploader/renderer.jsx we have to import the DrggableComponent then render it on the DOM, you should already be familiar with that.

/*renderer.jsx*/
import React from "react";
import ReactDOM from "react-dom";
//Uploader Component 
import DraggableUploader from "./draggableUploader";
//DOM Rendering 
ReactDOM.render(
  <DraggableUploader/>, document.getElementById("root"));

The moment of truth, you can start up a liveServer or simply open the index.html from dist/ on your browser you should see an awesome draggable image uploader container with awesome blueprintjs buttons.

Just Drag and Drop or select an image from your computer, take a look on the console tab you should see the Encoded Image pixels, at this point you be halfway through finishing the image uploader so stay tuned for the second part where we are going to finish the Application.

 

What's Next

If you have got any problems or errors while trying to get things to work please make sure to read carefully and copy the snippet code provided above to ensure a smooth learning experience.

Make sure to watch the second part which we are going to finish the Image Uploader Application and make fully working.

 

Share Tutorial

Made With By

Ipenywis Founder, Game/Web Developer, Love Play Games