Generate a Perfectly-Sized Cover Image with Cloudinary
Tags: webdev, tutorial, cloudinary, image
Published: 2020-05-14 18:49:38

---
title: Generate a Perfectly-Sized Cover Image with Cloudinary
tags: webdev, tutorial, cloudinary, image
description: How to generate the perfectly sized cover images, with any input image, using Cloudinary's URL based transformations, overlays, and effects.
canonical_url: https://joshuatz.com/posts/2020/building-a-perfectly-sized-dynamic-cover-image-with-cloudinary/
---

> 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 <a href="https://cloudinary.com/features/responsive_images" rel="noopener" target="_blank">an entire feature set built around this</a>, 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 <a href="https://dev.to/p/editor_guide" rel="noopener" target="_blank">*Editor Guide*</a>)
 - Twitter: (depends) 16:9 (1.77:1), 2:1, 1200x628 (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:

<img src="https://res.cloudinary.com/demo/image/upload/sample" alt="Sample Input" style="margin: auto; display:block; max-width: 800px; width: 70%; height: auto;" />

Sample ***goal*** output:

<img src="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" alt="Sample Output" style="margin: auto; display:block; max-width: 800px; width: 80%; height: auto;" />

> 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 <a href="https://cloudinary.com/documentation/image_transformations" rel="noopener" target="_blank">Cloudinary image transformations</a>, 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:
 1. Start with the "Base Image": `https://res.cloudinary.com/demo/image/upload/`***`sample`***
     - Added: ***`sample`***
    <img src="https://res.cloudinary.com/demo/image/upload/sample" style="margin: auto; display:block; max-width: 600px; width: 50%; height: auto;" />
 2. 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/`***
    <img src="https://res.cloudinary.com/demo/image/upload/c_fill,w_1000,h_420/sample" style="margin: auto; display:block; max-width: 600px; width: 50%; height: auto;" />
 3. 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 <a href="https://cloudinary.com/documentation/image_transformation_reference#effect_parameter" rel="noopener" target="_blank">is on a scale from 1 to 2000</a>
     - Opacity is on a scale from 0 to 100 (100 being completely opaque)
    <img src="https://res.cloudinary.com/demo/image/upload/c_fill,w_1000,h_420,e_blur:1500,o_75/sample" style="margin: auto; display:block; max-width: 600px; width: 50%; height: auto;" />
 4. 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 <a href="https://cloudinary.com/documentation/image_transformations#adding_image_overlays" rel="noopener" target="_blank">*image overlay* layer</a>.
    <img src="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" style="margin: auto; display:block; max-width: 600px; width: 50%; height: auto;" />
 5. 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*...
    <img src="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" style="margin: auto; display:block; max-width: 600px; width: 50%; height: auto;" />
 6. 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 <a href="https://cloudinary.com/documentation/transformation_flags" rel="noopener" target="_blank">the docs</a>:
            - > 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`***

Final output, using option B:

<img src="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" style="margin: auto; display:block; max-width: 800px; width: 90%; height: auto;" />

### 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 <a href="https://cloudinary.com/documentation/image_transformations#adding_text_captions" rel="noopener" target="_blank">overlay text</a>, 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
    <img src="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_:My%20Image,co_rgb:FFFFFF,bo_8px_solid_black,g_south,y_30/sample" style="margin: auto; display:block; max-width: 800px; width: 80%; height: auto;" />

## 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:
```js
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...):

<img src="https://i.imgur.com/UIi99Do.gif" alt="Cloudinary Dynamic Cover Image - Demo With Multiple Inputs" style="margin: auto; display:block; max-width: 1038px; width: 100%; height: auto;" />

Nice, right!?

We could also...

 - Save this as a <a href="https://cloudinary.com/documentation/chained_and_named_transformations#named_transformations" rel="noopener" target="_blank">*Named Transformation*</a>
     - Once we do this, reusing it becomes as easy as `/image/upload/{transformationName}/{uploadedId}`
 - 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:
>
> - <a href="https://cheatsheets.joshuatz.com/cheatsheets/apis/cloudinary/" rel="noopener" target="_blank">Cloudinary Cheatsheet</a> - WIP quick cheatsheet
> - <a href="https://joshuatz.com/projects/applications/desktop-cloud-transform-dct/" rel="noopener" target="_blank">Desktop Cloud Transform</a> - Transform local images with Cloudinary, with easy drag-and-drop GUI
> - <a href="https://joshuatz.com/projects/web-stuff/cloudinary-wysiwyg-visual-editor-for-transformations/" rel="noopener" target="_blank">Cloudinary WYSIWYG</a> - Visually build Cloudinary transformations with interactive canvas

---

More About Me:

 - 🔗<a href="https://joshuatz.com/" rel="noopener" target="_blank">joshuatz.com</a>
 - 👨‍💻<a href="https://dev.to/joshuatz" rel="noopener" target="_blank">dev.to/joshuatz</a>
 - 💬<a href="https://twitter.com/1joshuatz" rel="noopener" target="_blank">@1joshuatz</a>
 - 💾<a href="https://github.com/joshuatz" rel="noopener" target="_blank">github.com/joshuatz</a>