How To Easily Store And Display User Images In React

Looking at every option to store an image, it can get overwhelming. I wanted to show a straightforward and simple way to store an image from a user and display that image later on. I made this very barebones so that it can be customized to fit any web application with a server adn database.


  1. Set up Cloudinary
  2. Set up Image Uploading Component
  3. Create API Call
  4. Display Stored Image

This tutorial will assume you have a database set up where you can store the images’ UUIDs. If you don’t have a database yet, I highly recommend a Hasura PostgreSQL+GraphQL database. It has good documentation and tutorials on how to set up for React, along with compatibility with Auth0 authorization and authentication. Hasura is free and Auth0 has a very generous free plan.

To begin, I will introduce Cloudinary. It’s a video and image management service that can store, alter, and return images to and from your application. We will be using this as it has good documentation, easy to use API, and also has a great free plan.

1. Set up Cloudinary. To set up, go to and create an account. When you’re logged in, at the top you should see your Cloud Name, API Key, and API Secret. You need these to be able to access your storage.

Save these in your React app in the .env file.

In your root directory, I suggest you create a config.js file that extracts the .env items.

// In config.js
const dotenv = require('dotenv');
dotenv.config({path: __dirname + '/.env'});
exports.CLOUD_NAME = process.env.CLOUD_NAME || 'cloud-name';
exports.API_KEY = process.env.API_KEY || 'api-key';
exports.API_SECRET = process.env.API_SECRET || 'secret-key';

2. Create a Functional Component that takes in a user’s image file. We are using Functional Components instead of React Components in this example.

// ImageDisplay.js
import React, { useState } from 'react';
import axios from 'axios';
export default function ProfileImage() {
const [values, setValues] useState({
imagePreviewUrl: "",
picFile: null
let fileInput = React.createRef();

// Activates user file input to set div
const editProfilePic = () => {;
// Handles the image that was input by user
const handleImageChange = e => {
let reader = new FileReader();
let inFile =[0];
reader.onloadend = () => {
picFile: inFile,
imagePreviewUrl: reader.result
// Call the API Backend, will describe this later
const handleSubmit = async() => {
// response stores the response back from the API
response = await`/storage/upload`,form_data)
.catch(error => {
alert("Error occurred while uploading picture, try uploading a smaller image size or try again later.")
<div onClick={() => editProfilePic()}>
ref={fileInput} />
alt="..." style={{objectFit: 'cover'}}/>
<button onClick={handleSubmit}>Submit<button />

That’s a fat block of code right there, let me break it down.

Below we use a React Hook to store the temporary preview image URL of the user uploaded file and the actual image file itself. As a default, we set the URL to a temporary image on our server and the file to null.

const tempImage = require("assets/media/img/preview.jpg")
const [values, setValues] useState({
imagePreviewUrl: rempImage,
picFile: null

Now, because of our onClick function, if we click on this div (which consists of an image), it will prompt the user to input a file.

Once the file is submitted our handleImageChange function sets our preview URL to one created by the FileReader, and it sets our file to the submitted image. Once the URL is changed, you should see the uploaded picture on display! Unfortunately, this isn’t as impressive as showing the images that we stored from the user, so I’ll have to show you that in a bit.

Since we have the image we want to upload, we will use the Cloudinary SDK to upload pictures to your storage. We use Axios to make our API post call to the backend, but any HTTP request tool will do.

3. Call the Cloudinary API. If you don’t have a server yet for your React app it is simple to set up. Here is a tutorial on how to get you started.

Now, in your server, we must instantiate Cloudinary. Start by importing their libraries as well as your cloud name, API key, and secret key from your config file.

// dependencies
npm install multer --save
npm install multer-storage-cloudinary --save
npm install cloudinary --save
// In Server Api file, we'll call it storage.js.
var multer = require('multer');
const cloudinary = require("cloudinary");
const cloudinaryStorage = require("multer-storage-cloudinary");
const {
} = require('../config');

After this, set up the Cloudinary configuration to fit your storage. We use multer to handle the Form Data (image file) and we use multer-storage-cloudinary to create the storage definition of our file for Cloudinary.

In the code below, you can see we set the folder to “images”. If you want to create a folder you have to :
Sign into Cloudinary -> Click Media Library tab -> Create Folder
and name the folder “images” or whatever you’d like to call it.

We set the allowed input formats to be .jpg and .png but other types are supported, including video format.

Transformations are used when you want to edit the image before uploading it to Cloudinary. This is useful for reducing the size of the image to save space as well as adjusting the quality of the image to suit your needs. For this case, I set the width or height to 1900 if greater than that (“w_gt_1900” “h_gt_1900”, documentation here). We do 2 of these so the image scales instead of stretches. We also set the quality to “auto” so Cloudinary automatically reduces the image quality for optimum storage and faster loading speeds.

// in storage.js// Account access information
cloud_name: CLOUD_NAME,
api_key: API_KEY,
api_secret: API_SECRET
// Uploading Image Configuration
const storage = cloudinaryStorage({
cloudinary: cloudinary,
folder: "images",
allowedFormats: ["jpg", "png"],
transformation: [
{ if: "w_gt_1900", width: 1900, crop: "scale" },
{ if: "h_gt_1900", height: 1900, crop: "scale" },
{ quality: "auto" },
{ format: 'jpg' }
const parser = multer({ storage: storage });

Create the POST call. The parser handles the image and after it is uploaded we get a public_id in return. Store this ID in your database wherever you need to. It can be the “profile_pic_id” in the “user” table or the “mithril_axe_image_id” in the “weapons” table of your Runescape forum. The res.json is how we send a response back to our front end, so the front end can handle the database storage of the UUID if you’re using GraphQL (just like me!).

// in'/upload', parser.single("file"), (req, res) => {

const imageUUID = req.file.public_id;

//Code to store imageUUID in your database
// Return the UUID to the front end like this if necessary

🎉 Congratulations folks you have just successfully uploaded a file to Cloudinary. This part is honestly written everywhere, so let me show you how to then display it to your users.

4. Time to display our Cloudinary images to our user. All you need is the imageUUID from when you saved the image and Cloudinary’s library. There are 2 ways to handle this.

The first method is using the React SDK for Cloudinary —

// install
npm install cloudinary-react --save
// import in Component you need image in
import {Image, Video, Transformation, CloudinaryContext} from 'cloudinary-react';

Get the UUID of the image you want to display from your database. Then use Cloudinary’s Image tag to display the image, setting the “publicId” to the UUID you have. Replace YourCloudName with your actual cloud name from Cloudinary by the way.

<Image cloudName="YourCloudName" publicId="ImageUUIDFromDatabase"/>

Now you should be able to see your image!

The second method is instantiating a Cloudinary object and using its .url() function.

// In file where you want image to be displayed
var cloudinary = require('cloudinary/lib/cloudinary').v2
cloud_name: "YourCloudName"

Then you can use the Cloudinary object wherever you need a URL, for example:

const imageUUID = props.ImageUUIDFromDatabasereturn <img src={cloudinary.url(imageUUID, {secure: true, height: 600px, quality: "auto"})} />

You can also change the features of the image returned, here we make it secure, set the image height to 600px, and optimized the image quality so it arrives faster. The parameters for the library are here.

Hope this helps, I remember spending way too long trying to understand this before implementing so I hope I saved someone a couple of hours. Any questions or corrections let me know in the comments!




Hello! I’m Tan, a Computer Science graduate and entrepreneur. I try to write my mind on emerging economies and the potential of globalism in every sector.

Love podcasts or audiobooks? Learn on the go with our new app.

Recommended from Medium

🌍 Creating React usePosition() hook for getting browser’s geolocation

Advanced Drawing with Pixi.js

What Is The Scope In JavaScript

Develop React (Native) apps like a PRO

Vue’s Composition API Has Serious Issues

Important Features of ES6 with Examples

Ternary Conditional

NextAuth Part 3— Add firebase adapter in NextAuth

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store
Tan Arin

Tan Arin

Hello! I’m Tan, a Computer Science graduate and entrepreneur. I try to write my mind on emerging economies and the potential of globalism in every sector.

More from Medium

CORS policy Error In Working With Graphql, ReactJs,Apollo client.

Implement Analytics Dashboard in React using Custom Resolvers for MongoDB Realm GraphQL

Analytics Dashboard implement in React.js which is using data fetched from MongoDB Realm GraphQL

CORS policy Error In Working With Graphql, ReactJs,Apollo client.

Let’s understand Gatsby JS with a blog site