
Migrating Images to Cloudinary: How I Reduced My Repo Size by 99.7% and Supercharged My Website
Why I ditched local images and migrated everything to Cloudinary CDN in Next.js: less bloat, more speed, and a way more professional workflow.
The Local Images Problem
A month ago, my Next.js portfolio was like most others: loading images from /public/images
. The result?
- The repo weighed 37MB (committing images? Total junior move, but we’ve all done it).
- Build times were slow.
- Mobile users saw a slow, unoptimized site.
I wanted to fix this at the root, scale without fear, and have a site worthy of 2025. The solution: migrate everything to Cloudinary.
Why Cloudinary? (And Not Another CDN)
The Downside of Local Images:
- Every commit gets heavier.
- No automatic compression or modern formats.
- Not scalable if your site or image count grows.
- If you have multiple environments (dev, preview, prod), you can forget about image sync.
REAL Advantages of Cloudinary:
- Global CDN: Images are delivered from a server close to the user.
- Automatic optimization: Serves WebP, AVIF, and compresses by default.
- Dynamic transformations: Resize, crop, blur, all on the fly.
- Centralized dashboard: No more searching through endless folders.
Step by Step: How I Migrated Everything
1. Initial Setup
First, the dependencies:
npm install cloudinary next-cloudinary
And your secrets in .env.local
:
NEXT_PUBLIC_CLOUDINARY_CLOUD_NAME=your_cloud_name
CLOUDINARY_API_KEY=xxxx
CLOUDINARY_API_SECRET=xxxx
2. Custom Image Component
I created a component that automatically generates the Cloudinary src
, falls back to the local image if it fails, and leverages Next.js optimization:
// components/common/SimpleCloudinaryImage.tsx
const getCloudinaryUrl = (imageSrc: string) => {
const pathWithoutImages = imageSrc.replace('/images/', '').replace(/\.[^/.]+$/, '');
const cloudinaryId = `blacro-portfolio/${pathWithoutImages}`;
return `https://res.cloudinary.com/dm9driroe/image/upload/v1/${cloudinaryId}`;
};
And the import replacement:
// Before
import Image from 'next/image';
// After
import SimpleCloudinaryImage from '../../common/SimpleCloudinaryImage';
3. Bulk Uploading Images
I used Cloudinary’s API and dashboard to keep the structure:
/images/project/image.jpg → blacro-portfolio/project/image
TIP: Upload everything by folder, so paths never change and the code doesn’t break.
Challenges and How I Solved Them
SSR + Environment Variables
The CldImage
component gave errors at build because it tried to access server-only environment variables. Solution: I always render from the client using direct URLs.
priority vs loading in Next.js
If you set priority={true}
and loading="lazy"
together, Next.js throws an error. Solved it with:
loading={priority ? undefined : loading}
Bulletproof Fallbacks
If Cloudinary fails, the local image should always show. I implemented automatic fallback:
onError={() => {
console.error('Image failed to load:', imageSrc);
setImageError(true);
}}
The Numbers Don’t Lie
After the migration:
- Repo: From 37MB to 120KB (!).
- First Contentful Paint: 60% faster on mobile.
- SEO: Much better Core Web Vitals.
- DX: I can add images via drag & drop from the Cloudinary dashboard.
Best Practices I Implemented
- Single centralized component for all images.
- Consistent path structure: If the path changes, I only change it in one place.
- priority only for above-the-fold images, loading=“lazy” for the rest.
- Monitoring: Lighthouse before and after to see real impact.
Is Migration Worth It?
If you’re still loading local images, you’ll regret it sooner or later. Migration takes one day and you see instant benefits. Less bandwidth, fewer build issues, more speed, and your site feels way more pro.
My Recommendations:
- Plan your image structure before uploading anything.
- Implement fallbacks so UX never breaks if Cloudinary doesn’t respond.
- Centralize image handling in a single component.
- Always monitor performance after every change.
- Never upload local images to the repo again. Seriously.
Thinking about scaling your site or just want better performance? Migrating to Cloudinary or any image CDN should be on your checklist.
No way I’m ever going back.
Migration done with Next.js 15, Cloudinary, and a performance obsession.