DEV Community

Cover image for Image Upload Using Cloudinary + Multer in React.js & Node.js
Ishwor karki
Ishwor karki

Posted on

Image Upload Using Cloudinary + Multer in React.js & Node.js

📸 Image Upload Using Cloudinary + Multer in React.js & Node.js

Handling image uploads is something almost every web app needs to deal with — whether you're building an e-commerce platform, a blog, or a portfolio site. In this post, I’ll walk you through a simple and practical way to upload images from a React frontend to Cloudinary, using Multer in a Node.js + Express backend.

Let’s get started! 🚀


🧰 What We’re Using

  • Frontend: React.js
  • Backend: Node.js + Express.js
  • Middleware: Multer
  • Cloud Storage: Cloudinary

🤔 Why Use Cloudinary?

Cloudinary makes image management incredibly easy and efficient. Here’s why I chose it:

  • ✅ Automatically optimizes images for web delivery
  • ✅ Handles storage, transformation, and CDN delivery out of the box
  • ✅ Free tier is great for small apps or portfolios
  • ✅ Easy integration with Node.js and other platforms

You don’t have to worry about where images are stored, how they are served, or resizing them manually. Cloudinary handles all that for you.


✅ Step 1: Set Up Cloudinary

  1. Go to cloudinary.com and create a free account.
  2. Open your Dashboard.
  3. Take note of your Cloud name, API key, and API secret — you’ll need them soon.

🧱 Step 2: Backend Setup (Node.js + Express)

🔸 1. Install the packages

npm install express multer cloudinary dotenv cors
npm install multer-storage-cloudinary
Enter fullscreen mode Exit fullscreen mode

🔸 2. Create .env file

At the root of your project, create a .env file:

CLOUDINARY_CLOUD_NAME=your_cloud_name
CLOUDINARY_API_KEY=your_api_key
CLOUDINARY_API_SECRET=your_api_secret
Enter fullscreen mode Exit fullscreen mode

💡 Tip: Make sure to add .env to your .gitignore so you don’t commit your secrets.


🔸 3. Create server.js

import express from "express";
import cors from "cors";
import dotenv from "dotenv";
import uploadRouter from "./routes/uploadRoute.js";

dotenv.config();

const app = express();
app.use(cors());
app.use(express.json());
app.use("/api/upload", uploadRouter);

const PORT = 8000;
app.listen(PORT, () => console.log(`Server running on port ${PORT}`));
Enter fullscreen mode Exit fullscreen mode

🔸 4. Cloudinary Config - config/cloudinaryConfig.js

import { v2 as cloudinary } from "cloudinary";
import dotenv from "dotenv";

dotenv.config();

cloudinary.config({
  cloud_name: process.env.CLOUDINARY_CLOUD_NAME,
  api_key: process.env.CLOUDINARY_API_KEY,
  api_secret: process.env.CLOUDINARY_API_SECRET,
});

export default cloudinary;
Enter fullscreen mode Exit fullscreen mode

🔸 5. Upload Route - routes/uploadRoute.js

import express from "express";
import multer from "multer";
import { CloudinaryStorage } from "multer-storage-cloudinary";
import cloudinary from "../config/cloudinaryConfig.js";

const router = express.Router();

const storage = new CloudinaryStorage({
  cloudinary,
  params: {
    folder: "uploads",
    allowed_formats: ["jpg", "png", "jpeg"],
  },
});

const upload = multer({ storage });

router.post("/", upload.single("image"), (req, res) => {
  res.status(200).json({
    message: "Upload successful",
    imageUrl: req.file.path,
  });
});

export default router;
Enter fullscreen mode Exit fullscreen mode

🎨 Step 3: React Frontend

Here’s the UI component for selecting, previewing, and uploading the image.

🔸 ImageUploader Component

import React, { useState } from "react";
import axios from "axios";

const ImageUploader = () => {
  const [image, setImage] = useState(null);
  const [preview, setPreview] = useState("");
  const [url, setUrl] = useState("");

  const handleFileChange = (e) => {
    const file = e.target.files[0];
    setImage(file);
    setPreview(URL.createObjectURL(file));
  };

  const handleUpload = async () => {
    const formData = new FormData();
    formData.append("image", image);

    try {
      const res = await axios.post("http://localhost:8000/api/upload", formData);
      setUrl(res.data.imageUrl);
    } catch (error) {
      console.error("Upload failed:", error);
    }
  };

  return (
    <div>
      <h2>Upload an Image</h2>
      <input type="file" onChange={handleFileChange} accept="image/*" />
      <button onClick={handleUpload}>Upload</button>

      {preview && <img src={preview} alt="Preview" width="200" />}
      {url && (
        <div>
          <p>Uploaded Image URL:</p>
          <a href={url} target="_blank" rel="noopener noreferrer">{url}</a>
        </div>
      )}
    </div>
  );
};

export default ImageUploader;
Enter fullscreen mode Exit fullscreen mode

🚀 What You Get

  • ✅ A React UI that previews the image before upload
  • ✅ Images uploaded and stored in Cloudinary
  • ✅ The image URL returned and displayed

🧼 Bonus Tip

Only allow image files with this:

<input type="file" accept="image/*" />
Enter fullscreen mode Exit fullscreen mode

🙌 Final Thoughts

We just built a real-world image upload feature with:

  • ⚙️ Multer to process file uploads
  • ☁️ Cloudinary to store and optimize them
  • ⚛️ React to build the user interface

This setup is clean, scalable, and ready to be reused in any modern project. Whether you’re working on a blog, CMS, or e-commerce platform — this pattern will save you time and keep your codebase organized.


Thanks for following along — and happy coding! ✨

Top comments (1)

Collapse
 
ramcpucoder profile image
Ram Kumar Dhimal

loved it