For those unfamiliar with Cloudinary, it is a cloud service that is focused on image serving, with unique features like transformations, responsive generation, and a speedy CDN for delivery. I have no affiliation with Cloudinary, other than being a fan of their service.
Introduction
One of the big changes in web development over the years has been a shift from “fixed” designs to “responsive”. For example, where we used to define layouts or the display of assets with fixed units, we now try to use percentages or other ratio based approaches to adapt to any display size. This is something to be celebrated, and Cloudinary has an entire feature set built around this, but today I want to talk about when the opposite approach is needed.
We often have “cover images”, “hero images”, and/or images that we need for social media sharing sites, and these frequently have specific desired pixel sizes or aspect ratios. For example:
- Dev.to cover image: 1000 x 420 (see Editor Guide)
- Twitter: (depends) 16:9 (1.77:1), 2:1, 1200×628 (1.91:1)
- Facebook feed: 1.91:1
Finding images that perfectly fit these aspect ratios is often difficult and time-consuming, and manually cropping and resizing existing images to fit, equally so.
What if there was a better way to generate the perfectly sized hero image, with any image as the input?…
Our Goal
So, here is our example goal. We want to produce a 1000 x 420 pixel (2.38:1) cover image for Dev.to, but do so automatically, with a method that accepts any image as the input. If the input aspect ratio does not match the desired output, then the input will be centered, and a blurred background based on the same input shown behind it.
Sample Input:
Sample goal output:
The cover image for this very post was created with the method outlined below! But, you’ll see that later.
Building It With Cloudinary
I’m going to walk through how to build this with Cloudinary image transformations, using URL parameters alone. This makes it very easy to use anywhere, with any backend or frontend, since all we need is a URL.
Base Image: https://res.cloudinary.com/demo/image/upload/sample
When you see Bold and Italic text in the URL, that indicates what was added in the step.
Steps:
- Start with the “Base Image”:
https://res.cloudinary.com/demo/image/upload/
sample
- Added:
sample
- Added:
- Use image to fill exact desired size
https://res.cloudinary.com/demo/image/upload/
c_fill,w_1000,h_420/
sample
- Added:
/c_fill,w_1000,h_420/
- Since this is actually going to be the background, apply a blur effect and reduce opacity
https://res.cloudinary.com/demo/image/upload/c_fill,w_1000,h_420
,e_blur:1500,o_75
/sample
- Added:
e_blur:1500,o_75
- Blur is on a scale from 1 to 2000
- Opacity is on a scale from 0 to 100 (100 being completely opaque)
- Overlay the same image, but limit its size to the “canvas”
https://res.cloudinary.com/demo/image/upload/c_fill,w_1000,h_420,e_blur:1500,o_75
/l_sample,c_limit,w_1000,h_420/
sample
- Added:
/l_sample,c_limit,w_1000,h_420/
- Notice how
l_{uploadedId}
is used to generate an image overlay layer.
- Add a nice blurred border to our overlay
https://res.cloudinary.com/demo/image/upload/c_fill,w_1000,h_420,e_blur:1500,o_75/l_sample,c_limit,w_1000,h_420
,e_outline:outer:2:100
/sample
- Added:
e_outline:outer:2:100
- You might notice that the border now pushes the overlay outside the canvas…
- Prevent “overflow” caused by new border
- Adding a border to the overlay actually made it larger than the boundaries of the background (base image). We have two options to remedy this:
- Option A: Re-crop: Since adding a border to the overlay made it larger than the boundaries of the background, we can simply re-crop the output
https://res.cloudinary.com/demo/image/upload/c_fill,w_1000,h_420,e_blur:1500,o_75/l_sample,c_limit,w_1000,h_420,e_outline:outer:2:100
/c_crop,w_1000,h_420/
sample
- Added:
/c_crop,w_1000,h_420/
- If we wanted to re-crop just the overlay, we could use
fl_layer_apply
to prevent crop action from applying to the entire chain
- Option B: Use the
no_overflow_flag
- This is a super helpful flag to remember! From the docs:
Prevents Cloudinary from extending the image canvas beyond the original dimensions when overlaying text and other images.
https://res.cloudinary.com/demo/image/upload/c_fill,w_1000,h_420,e_blur:1500,o_75/l_sample,c_limit,w_1000,h_420,e_outline:outer:2:100
,fl_no_overflow
/sample
- Added:
fl_no_overflow
- This is a super helpful flag to remember! From the docs:
Final output, using option B:
Extras:
There is still more we can do with this!
- Add compression
- To save some bytes, we can tell Cloudinary to serve as a compressed JPG with a quality of 80%
https://res.cloudinary.com/demo/image/upload/c_fill,w_1000,h_420,e_blur:1500,o_75/l_sample,c_limit,w_1000,h_420,e_outline:outer:2:100/c_crop,w_1000,h_420
,q_80
/sample
- For our sample image, this gives us a saving of approximately 50%!!!
- Add overlay text
- We can add overlay text, such as the title of the post that our cover image is for
https://res.cloudinary.com/demo/image/upload/c_fill,w_1000,h_420,e_blur:1500,o_75/l_sample,c_limit,w_1000,h_420,e_outline:outer:2:100,fl_no_overflow
/l_text:Roboto_100_normal_stroke_:Hero%20Image,co_rgb:FFFFFF,bo_8px_solid_black,g_south,y_30/
sample
- stroke, and border (
bo_
) are used to provide an outline around the text
Wrap-Up
This might have seemed like a bunch of work, but don’t forget; now that we have the transformation chain built, we can reuse it across an unlimited number of images, simply replacing {uploadedId}
with our desired input:
const myCloudId = 'acb123';
// pretend `myImages` is an array of hundreds of image IDs
myImages.forEach(uploadedId => {
const heroImage = `https://res.cloudinary.com/${myCloudId}/image/upload/c_fill,w_1000,h_420,e_blur:1500,o_75/l_${uploadedId},c_limit,w_1000,h_420,e_outline:outer:2:100,fl_no_overflow/${uploadedId}`;
saveImage(heroImage);
});
In fact, here it is in action, working its magic across any input I give it (large GIF, give a second to load…):
Nice, right!?
We could also…
- Save this as a Named Transformation
- Once we do this, reusing it becomes as easy as
/image/upload/{transformationName}/{uploadedId}
- Once we do this, reusing it becomes as easy as
- Implement this via Cloudinary API
- Integrate into any backend (WordPress, Gatsby, etc.) for dynamic
meta
image tags to serve to Twitter, FB / OpenGraph, etc. - … and many more things!
If this was interesting to you, I have a few other Cloudinary related projects that might be worth checking out:
- Cloudinary Cheatsheet – WIP quick cheatsheet
- Desktop Cloud Transform – Transform local images with Cloudinary, with easy drag-and-drop GUI
- Cloudinary WYSIWYG – Visually build Cloudinary transformations with interactive canvas
More About Me: