Cloudinary in Astro: External Image CDN for Content Sites That Scale

What Cloudinary is, how to use it in Astro, how URL-based transforms work, and when to switch from local images to an external CDN — with real examples from astro-content-lab.

Quick answer

How do you use Cloudinary images in Astro?

Upload images to Cloudinary, store the URL as a string field in your frontmatter, and use a plain img tag or Cloudinary's URL transform parameters to display them. No special integration needed — Cloudinary works as a standard image URL in any Astro template.

Cloudinary dashboard showing uploaded images with transformation URL examples
First-hand experience: Based on direct hands-on use. The Cloudinary setup in this article was tested on astro-content-lab using a free tier account. Cloud name: di9xkgxd8.

When I first heard about Cloudinary, I thought it was another image hosting site. Like Imgur but for professionals. Upload a photo, share a link.

That’s technically correct but completely misses the point.

Cloudinary is an image CDN with a transformation API built into the URL. You don’t need a server, you don’t need to write code to resize images, you don’t need to generate thumbnails. You just change the URL.

That realization hit me when I saw the transform parameters for the first time — w_800,h_400,c_fill,f_auto,q_auto embedded directly in a URL. I’d seen URLs like that on other sites before and always wondered what those strange parameters meant.

Now I know. And it’s genuinely clever.


What Cloudinary actually is

Cloudinary is three things in one:

Storage — your images live on Cloudinary’s servers, not in your repo or on your hosting.

CDN — images are served from edge servers close to visitors. Someone in Europe gets the image from a European server. Someone in Asia gets it from an Asian server.

Transformation API — every image can be resized, cropped, reformatted, and compressed on demand by changing the URL. No image editing software needed. No server code needed.

For a static Astro site, the last point is particularly valuable. Astro optimizes local images at build time — which is great, but means every size variant gets generated during the build. Cloudinary generates variants on demand, at the CDN edge, without touching your build.


Setting up Cloudinary

Step 1: Create a free account

Go to Cloudinary and create an account. Cloudinary currently measures Free-plan usage in credits rather than promising separate fixed storage and bandwidth quotas. Storage, delivery bandwidth, and newly generated transformations all use that allowance.

That model can still be practical for a small content site, but check the current Cloudinary plans before treating a quota as permanent.

Step 2: Find your cloud name

After signing up, your cloud name appears in the dashboard — top left corner. It looks like di9xkgxd8 or a custom name you chose during registration.

This is part of every Cloudinary URL. Keep it handy.

Step 3: Upload your first image

Go to Media Library → click Upload → drag and drop an image file.


Understanding the Cloudinary URL

When you upload an image, Cloudinary gives you a URL like this:

https://res.cloudinary.com/di9xkgxd8/image/upload/v1781786068/hello-world.jpg

Breaking it down:

https://res.cloudinary.com/  → Cloudinary CDN domain
di9xkgxd8/                  → your cloud name
image/upload/                → resource type and action
v1781786068/                 → version number (auto-generated)
hello-world.jpg              → your filename

That URL serves the original image. Now the interesting part.


URL transforms — the magic part

Between /upload/ and the version number, you can insert transformation parameters:

https://res.cloudinary.com/di9xkgxd8/image/upload/[TRANSFORMS]/v123/hello-world.jpg

Common transforms:

w_800          → width 800px
h_400          → height 400px
c_fill         → crop to fill (smart cropping)
c_fit          → fit within dimensions (no cropping)
f_auto         → serve WebP to browsers that support it, JPEG otherwise
q_auto         → automatic quality optimization

Combine them with commas:

# Resize to 1200x630, WebP, auto quality — perfect for hero images
w_1200,h_630,c_fill,f_auto,q_auto

# Small thumbnail, 400x210
w_400,h_210,c_fill,f_auto,q_auto

Full hero image URL with transforms:

https://res.cloudinary.com/di9xkgxd8/image/upload/w_1200,h_630,c_fill,f_auto,q_auto/v1781786068/hello-world.jpg

Using Cloudinary in Astro

The simplest way: store the URL as a string in frontmatter.

Step 1: Add a Cloudinary field to the schema

const posts = defineCollection({
  loader: glob({ pattern: '**/*.{md,mdx}', base: './src/content/posts' }),
  schema: ({ image }) => z.object({
    title: z.string(),
    description: z.string(),
    publishedAt: z.date(),
    heroImage: image().optional(),           // local image
    heroImageCloudinary: z.string().optional(), // cloudinary URL
    heroImageAlt: z.string().optional(),
  }),
})

Having both fields lets you use local images during development and Cloudinary in production — or mix both depending on the post.

Step 2: Add the URL to frontmatter

---
title: What is a Static Site?
heroImageCloudinary: https://res.cloudinary.com/di9xkgxd8/image/upload/w_1200,h_630,c_fill,f_auto,q_auto/v1781786068/hello-world.jpg
heroImageAlt: Static site generation explained
---

Put the transforms directly in the URL you store. This way the frontmatter always contains the final, optimized URL — no transformation logic needed in the template.

Step 3: Display in the template

{post.data.heroImage ? (
  <div class="hero-image">
    <Image
      src={post.data.heroImage}
      alt={post.data.heroImageAlt || post.data.title}
      width={1200}
      height={630}
    />
  </div>
) : post.data.heroImageCloudinary ? (
  <div class="hero-image">
    <img
      src={post.data.heroImageCloudinary}
      alt={post.data.heroImageAlt || post.data.title}
      width={1200}
      height={630}
      loading="lazy"
    />
  </div>
) : null}

Local image → uses Astro <Image /> component for build-time optimization. Cloudinary image → uses plain <img> tag since Cloudinary already handles optimization via URL.


Local vs Cloudinary: honest comparison

Local (src/assets/)Cloudinary
SetupZero — just add filesFree account + upload
OptimizationAstro at build timeCloudinary at CDN edge
Repo impactImages committed to GitNo repo impact
Transform controlFixed at build timeDynamic via URL
CDNVercel/Netlify CDNCloudinary global CDN
CostFreeFree tier, then paid
Best forSmall-medium sitesHigh-volume content

When to use which

Use local images when:

  • Your site has fewer than ~100 posts
  • You prefer everything in one place
  • Build times aren’t a concern
  • The repo size is manageable

Use Cloudinary when:

  • Your repo is getting heavy from images
  • You publish frequently with multiple images per post
  • You need the same image at many different sizes
  • You want to serve the absolute best format per browser automatically

The practical path:

Start with local images. They’re simpler. When the repo starts feeling heavy or build times creep up, migrate to Cloudinary — update the frontmatter fields, remove the local files, done. The templates handle both.

This is exactly how astro-content-lab is set up: local images for the demo content, Cloudinary support ready for when it’s needed.


A note on repo size as your site grows

This came up when building this project: what happens when you have hundreds of images in src/assets/?

The repo grows. Every git clone takes longer. Every new developer on the project downloads megabytes of images they may not need.

Some teams solve this with Git LFS (Large File Storage) — a Git extension that stores large files separately. It works but adds complexity.

The cleaner solution for a content site: move images to Cloudinary when the repo hits a size that slows you down. Commit only code and content. Let the CDN handle the media.

There’s no hard rule on when to make the switch. When cloning the repo starts taking noticeably longer than it should, that’s the signal.


Frequently Asked Questions

Is Cloudinary free for Astro sites?
Cloudinary offers a Free plan measured in monthly credits. Storage, delivery bandwidth, and newly generated transformations all consume that allowance, so the practical capacity depends on your image sizes and traffic. Check Cloudinary's current pricing page before choosing it for a production site.
What are Cloudinary URL transforms?
Transformation parameters embedded in the Cloudinary URL path. Add w_800,h_400,c_fill between /upload/ and the version number to resize and crop. Add f_auto,q_auto to serve WebP automatically at optimized quality. The transformed image is generated on first request and cached at the CDN edge — no build step needed.
When should I switch from local to Cloudinary?
Switch when repo size starts causing friction — slow clone times, heavy build, hundreds of images to manage. For a personal blog under 100 posts, local images work fine. For a content site publishing regularly with multiple images per post, Cloudinary keeps the repo lean and builds fast.
Do I need a special Astro integration for Cloudinary?
No. Cloudinary images are just URLs — store them as strings in frontmatter, display with a plain img tag. Add transform parameters directly to the URL before storing it. No integration package required. The simplest approach works perfectly.
What is the difference between local images and Cloudinary in Astro?
Local images in src/assets/ are optimized at build time and committed to Git. Cloudinary images live outside the repo, served from Cloudinary's global CDN, with transforms applied via URL parameters. Local is simpler to start. Cloudinary scales better as the content library grows.