Allowing Users to Upload Images in Your React App

Kroonmackenzie
5 min readDec 16, 2020

--

For a recent project I wanted to find a way to allow users to upload, crop, and resize images. If you are creating an app or website, this feature is crucial if you are creating user profiles. It may seem like a daunting task, but luckily there are several helpful tools that we can use to get this done. For this project, I am using the PERN stack.

We can start by using Create React App. This will set up a great framework for us to work off of.

npx create-react-app my-app
cd my-app
npm start

Once we have created a new project we can start creating the component that will handle uploading photos. React avatar editor is a great first step.

React avatar editor is a package that allows us to upload an image. It also allows the user to zoom in on their photo and crop it to the viewing area. React avatar editor be a component in our project. We can customize the way it looks so fit better with the styling of our project.

Let’s install our package:

npm install — save react-avatar-editor

Let’s create a new component to handle our upload functionality. Copy and paste the following code to get started:

import React from "react";import ReactAvatarEditor from "react-avatar-editor";class UploadImage extends React.Component {constructor(props) {super(props);this.state = {image: "",allowZoomOut: false,position: { x: 0.5, y: 0.5 },scale: 1,rotate: 0,borderRadius: 50,preview: null,width: 330,height: 330,};this.handleSubmit = this.handleSubmit.bind(this);}handleNewImage = (e) => {this.setState({ image: e.target.files[0] });};handleScale = (e) => {const scale = parseFloat(e.target.value);this.setState({ scale });};handlePositionChange = (position) => {this.setState({ position });};setEditorRef = (editor) => (this.editor = editor);
async
handleSubmit(e) {
if (this.editor) {// This returns a HTMLCanvasElement, it can be made into a data URL or a blob,// drawn on another canvas, or added to the DOM.const img = this.editor.getImageScaledToCanvas().toDataURL();}}render() {return (<div><div><ReactAvatarEditorref={this.setEditorRef}scale={parseFloat(this.state.scale)}width={this.state.width}height={this.state.height}position={this.state.position}onPositionChange={this.handlePositionChange}rotate={parseFloat(this.state.rotate)}borderRadius={this.state.width / (100 / this.state.borderRadius)}image={this.state.image}color={[255, 255, 255, 0.6]}className="editor-canvas"/></div><br /><label><inputname="upload-img-input"type="file"onChange={this.handleNewImage}/><h3>Upload Photo</h3></label><br /><h3>Zoom</h3><inputname="scale"type="range"onChange={this.handleScale}min={this.state.allowZoomOut ? "0.1" : "1"}max="2"step="0.01"defaultValue="1"/><div><button onClick={this.handleSubmit}>SUBMIT</button></div></div>)}}export default UploadImage;

If you open the browser you will see that we already have our main structure. Clicking ‘Choose File’ will let the user view images on their computer and select the photo that we want to upload. Once the user selects a photo, we see the preview of the selected photo.

We also have the option to zoom in on the photo and reposition it until we are satisfied. Right now our submit button is not hooked up to anything. First let’s console.log the variable img inside of our handle submit function. We need to know what data type we are dealing with before we design the rest of our handle submit function.

console.log(img)

You may think this is an error or that we did something wrong. Not at all! This is our image. After further inspection we can console.log(typeof img) and conclude that this is a string. This string is way too long! If we try to send this to a database we will likely receive an error that it is too large. What can we do with this?

Introducing Cloudinary. Cloudinary is a very useful tool that will allow us to store our large data strings in their cloud server. You need to set up an account which will give you three things you need to use Cloudinary in your app.

  1. Cloud name
  2. API key
  3. API secret

For my backend, I am using Node, Express, Sequelize, and Postgres.

You’ll need to set up a file to store your details from Cloudinary:

const cloudinary = require('cloudinary')cloudinary.config({cloud_name: 'your_cloud_name',api_key: 'your_api_key',api_secret: "your_api_secret"})module.exports = cloudinary

In your api folder you can create a file for this request to cloudinary. Our upload preset is something we can define in our Cloudinary account. The variable fileStr represents the large string we get from our Upload Image component. Sending a request to cloudinary.uploader.upload will save this string to our account. We can view all of the images that have been sent to our account. The variable secureUrl represents the response we get after sending our image to Cloudinary. This will be a https link that can successfully be sent to our Postgres database. It will now be like any image we find on the internet.

const router = require('express').Router()const cloudinary = require('../cloudinary')router.post('/', async (req, res, next) => {try {const fileStr = req.body.dataconst uploadResponse = await cloudinary.uploader.upload(fileStr, {upload_preset: 'Upload',})let secureURL = uploadResponse.secure_url} catch (error) {next(error)}})module.exports = router

We have everything set up, but if you try to upload an image nothing will happen. That’s because we still have not connected our frontend to our backend. We need to add a fetch inside of our handleSubmit function in our Upload Image component.

await fetch(`/api/upload`, {method: 'POST',body: JSON.stringify({data: img}),headers: {'Content-type': 'application/json'},})

This will send the data URL inside of an object to our api route. Our api route receives this object and plucks it off of req.body. Then we send it up to Cloudinary.

Cloudinary is a great way to store large data files and it also works great with Postgres to allow us to upload new photos or update existing photos. React avatar editor is easy right out of the box. These two tools let us create a way for users to easily upload, crop, and resize photos to their liking.

--

--