๐ธ 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