📸 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
- Go to cloudinary.com and create a free account.
- Open your Dashboard.
- 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
🔸 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
💡 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}`));
🔸 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;
🔸 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;
🎨 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;
🚀 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/*" />
🙌 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)
loved it