Astro is gaining momentum as an excellent framework for building high performance, modern websites. And while it has excellent image handling built in, this tutorial will show you 4 reasons why framework-specific image handling might let you down on professional sites — whether it's Astro, Next.js, Nuxt, or any of the options out there.
You'll learn how Cloudinary and Unpic can be added to any Astro site in minutes, and how they not only solve these 4 challenges, but add an additional 3 bonus features that will take your site image management to the next level.
So grab your favorite code editor and come along with me, your friend Jason, while we build pro Astro sites.
Get set up for development
Before we can get started, you'll need an Astro project. You can set a new Astro site up from scratch (npm create astro@latest
) if you'd like, or if you want to follow along with this tutorial step-by-step you can fork the starter repo:
This is a basic Astro site with pages and TODO comments in place to guide the project.
The built-in solution is great, but limited
Managing images in Astro can be done with no dependencies. Their built-in Image
and Picture
components are great.
Let's look at an example of optimizing a local image using Astro's built-in Image
component. We'll be using this image by Karsten Winegeart for our examples.
Edit src/pages/01-basic.astro
and put the following inside:
This generates HTML similar to the following:
This is responsive, performant image code! However, when we start thinking about this across deploys and across web properties, there are a few things about this approach that might not be desirable:
- The image is processed and delivered as a local URL, so its cache will be lost on every deploy, even if the image doesn't change. This can lead to incremental bandwidth usage and slower page loads for users.
- If you have multiple web properties that use this image, each one will have a different cache, resulting in unnecessary bandwidth and download times.
- The developer needs to manually add the
widths
attribute on each image to create asrcset
. This is a small additional step that must be taught to every teammate and checked for every image. - There's no support for placeholder images. This is a nice-to-have, but without it there are blank spaces in the layout while images load.
In many cases, none of these present a significant issue. With that being said, we can eliminate all the above challenges with very little effort — so in my mind, it's worth the extra step.
Use Astro + Cloudinary + Unpic for better image caching, performance, and developer experience
To enhance image management on an Astro site, we're going to use Cloudinary and Unpic (created by Astro team member Matt Kane). This combination of tools eliminates the challenges of the built-in solution and provides additional features:
- Using the same media CDN/UI components as other web properties in the company
- Automatic
srcset
for without devs needing to know about or add extra attributes - Automatic placeholders while images load
- Watermarking, overlays, and other image transformations
- Handling user uploads of images
- Advanced features, such as object-aware cropping, automatic alt text, and auto-moderation for user-uploaded images
I'll cover how to use these extra features in another tutorial. For now, let's look at how to set up the basics of Cloudinary + Unpic in an Astro project.
Step 1: Add your images to Cloudinary
If you don't already have a Cloudinary account, set up a new account on the free tier. Upload an image (Karsten Winegeart's photo will work again here) to your Cloudinary console and copy the public ID.
NOTE: Depending on when you created your Cloudinary account, public IDs may or may not include the folder or folders the image is stored in. If you're not sure, see the Cloudinary guide on finding the public ID.
Add your credentials as environment variables
For this tutorial, all we need is our Cloudinary cloud name. This is visible on your Cloudinary dashboard in the top left, as well as on the API keys page in your settings.
Since the image URLs will be generated server-side (either at build time or at request time, depending on which output mode your Astro site is using, we don't need to make this available to the client.
Step 2: Install the required dependencies and update the config
Next, install the required dependencies in your Astro site:
cloudinary
is the Cloudinary Node SDK@unpic/astro
is the Astro implementation of the Unpic image management toolkit
Step 3: Update the image to use Cloudinary
Cloudinary's SDK provides helpers to generate URLs for our images. And while we can manually build URLs for our images, that can get pretty challenging as more advanced transformations come into play.
NOTE: We'll get into more advanced transformations in future tutorials. If you want to learn more, check out the Cloudinary image transformation docs.
Make the following updates to src/pages/01-basic.astro
:
In the call to cloudinary.url()
, we include an object with a transformation
property. While it's optional, one of the most powerful features of Cloudinary is that we can set quality
and format
to auto
to automatically reduce the file size by both lowering the image quality to a level that isn't detectable by the human eye and by delivering in modern formats like WebP, AVIF, and others when the browser is capable of displaying them.
This is a huge win. Our original image weighs in at a whopping 2 MB. After adding automatic quality and format transformations, Cloudinary ends up delivering an asset that is only 119 kB before we get into responsive image sizes. Once those come into play, users on the smallest viewports will only have to download 13.1 kB — that's 99.3% smaller than the image we downloaded from Unsplash with barely any added code.
Now we get the following HTML output:
...wait. Where did our image CDN go?
It turns out, Astro's Image
component will process external images, but it does so by pulling them in at build time and turning them into processed, local assets.
This is why, if we want to fully optimize our media delivery, we also need to include Unpic.
Step 4: Update to Unpic
First, update the Astro config to use Unpic in astro.config.mjs
:
Once this is saved, update the import in src/pages/01-basic.astro
to get Image
from Unpic instead:
Unpic will automatically generate the srcset
, so we no longer need the widths
attribute. And to show a placeholder on slower connections while images download, the placeholder
attribute is set to blurhash
, which is built into Unpic.
After building the site, the HTML for this image now looks like the following:
With minimal changes to our code, we now have modern, responsive, properly-sized-for-all-browsers, showing-placeholders-on-slow-connections images — and because we're using Cloudinary + Unpic, this will work in all of your web properties, with support for many frameworks: Angular, Astro, Lit, Preact, Qwik, React, Solid, Svelte, Vue, or WebC, and the Cloudinary CDN means images share a cache cross-property.
Recap: why Cloudinary + Unpic + Astro rules
At this point we've barely dipped our toes into the capabilities of Cloudinary, but already we've got 4 big wins — all for a few minutes' worth of work:
- Our images are now served from the Cloudinary CDN, so the cache for unchanged images will survive between deploys.
- Our images can now be cached across multiple web properties by using the same URLs for delivery.
- Our images are automatically optimized for file size and file type, giving us up to a 99% savings on our image sizes with no extra effort on our part.
- Our images now have placeholders when a slow network means our images need more time to download.
In future tutorials we'll go further with Cloudinary and learn how we can:
- Add image overlays, watermarks, and other transformations to our images
- Handle user generated content by allowing images to be uploaded
- Automatically tag images and add image descriptions using AI tooling
- Deal with moderation for user-generated content, both using human moderators and AI tooling
- Leverage AI tooling to add smart cropping to images for better thumbnails
Resources and further reading
- Cloudinary's Astro image component — this was released after I wrote this post. It achieves the same goals with even less work
- Unpic
- Astro's image docs
- More info on content negotiation (how Cloudinary chooses the best file format to deliver for each request)