Compose image thumbnail with Cloudinary and Vue
Tags: vue, javascript, typescript, tutorial
Published: 2023-07-18 07:57:40

---
title: "Compose image thumbnail with Cloudinary and Vue"
published: true
description: "How to build a composable and create a optimized thumbnail for an image with Cloudinary in Vue app"
tags: ['Vuejs', 'JavaScript', 'TypeScript', 'tutorial']
cover_image: https://res.cloudinary.com/mayashavin/image/upload/v1689666754/articles/cloudinary/vuecloudinary.png
series: Image optimization with Cloudinary
canonical_url: https://mayashavin.com/articles/thumbnail-with-cloudinary-composable
---

One popular solution for image optimization is to use a CDN to serve the optimized variants of an image depending on the application's needs on run time. However, building a suitable variant can sometimes be a challenge. In this post, we will explore how to build a Vue composable for creating an image thumbnail dynamically from a Cloudinary-based image URL with Cloudinary and composition API.

## Table of Content

- [Table of Content](#table-of-content)
- [Prerequisites](#prerequisites)
- [Setting up Cloudinary with cloudinary-build-url package](#setting-up-cloudinary-with-cloudinary-build-url-package)
- [Building the Cloudinary composable](#building-the-cloudinary-composable)
- [Create a thumbnail with useCloudinary composable](#create-a-thumbnail-with-usecloudinary-composable)
- [Summary](#summary)

## Prerequisites

This post assumes you have a basic understanding of Vue 3 Composition API and a Cloudinary account (you can create one for free [here](https://cloudinary.com/users/register/free)). We will need a working Vue project,  which we can create with the following command:

```bash
npm init vue@latest
```

We will use TypeScript in this tutorial, so select it when prompted. Once created, we can run the project with the command `yarn dev` and be ready to move on.

## Setting up Cloudinary with cloudinary-build-url package

Cloudinary is a cloud-based service offering images and videos end-to-end solutions, such as storing, manipulating, optimizing, and delivering through a fast CDN and its URL-based API. Many SDKs and libraries are available in the community aiming to provide a good DX (Developer Experience) when working with Cloudinary. This tutorial will install and use the [Cloudinary Build URL](https://cloudinary-build-url.netlify.app/), a lightweight TypeScript URL builder for Cloudinary transformations on a specific image.

Let's install the package with the following command:

```bash
yarn add cloudinary-build-url
```

We also add TypeScript support using the below command:

```bash
yarn add -D @cld-apis/types
```

Once done, we are ready to build our Cloudinary composable, which we will do next.

## Building the Cloudinary composable

We will create a new file called `useCloudinary.ts` in our `composables` folder and start by importing the `setConfig` method from the `cloudinary-build-url` package to initialize our instance with our Cloudinary cloud name:

```ts
//composables/useCloudinary.ts
import { setConfig } from 'cloudinary-build-url'

setConfig({
  cloudName: 'your-cloud-name',
})
```

To find your Cloudinary cloud name, navigate to your [Cloudinary console - Programmable Media Dashboard](https://cloudinary.com/console) and copy the value of the `Cloud name` field, as seen in the below screenshot:

<img src="https://res.cloudinary.com/mayashavin/image/upload/q_auto,f_auto/v1629659840/articles/cloudinary/cloudinary_cloud_name" alt="Cloudinary cloud name" class="mx-auto" />

Next, we will create our composable - `useCloudinary` with the following code:

```js
//composables/useCloudinary.ts
import { setConfig, buildImageUrl, extractPublicId } from 'cloudinary-build-url'

//...
export const useCloudinary = (fullPath: string) => {
    const publicId = extractPublicId(fullPath);
    
    const transformations = ref({});

    const url = computed(() => {
        return buildUrl(publicId, {
            transformations: transformations.value,
        });
    });

    return {
        url,
        transformations,
    }
}
```

In the code above, we use `extractPublicId` to [extract the public ID of the image path](https://cloudinary-build-url.netlify.app/usage/extractPublicId) copied from the Cloudinary console. The `transformations` is a reactive object that we will use to dynamically store the transformations applied to the image. And we then use `buildImageUrl` to build the image's `url` from the public ID and the given transformations.

The composable then exposes the `url` and `transformations` to the consuming component. We will use these values to display the image in the component next.

## Create a thumbnail with useCloudinary composable

We will create a new component called `ImageDemo.vue` in the `components` folder, which accepts `src` and `alt` as props for displaying an image, with the following code:

```js
<template>
    <img :src="src" :alt="alt" />
</template>
<script setup>
const props = defineProps({
    src: {
        type: String,
        required: true,
    },
    alt: {
        type: String,
        required: true,
    },
});
</script>
```

Next, we import `useCloudinary` and get `url` and `transformations` to build the image URL from the given `src` as follows:

```html
<!--components/ImageDemo.vue-->
<script setup>
import { useCloudinary } from '../composables/useCloudinary.ts'

//...
const { url, transformations } = useCloudinary(props.src)
</script>
```

We also replace the `src` in the template with `url`:

```html
<template>
    <img :src="url" :alt="alt" />
</template>
```

And let's add a method to add a crop thumbnail method to the `transformations` object as follows:

```html
<template>
    <!--...-->
    <button @click="createThumbnail">Create round thumbnail</button>
</template>
<script setup>
import { RESIZE_TYPES } from '@cld-apis/utils'
//...

const createThumbnail = () => {
    transformations.value = {
        resize: {
          type: RESIZE_TYPES.FILL,
          width: 200,
          height: 200,
        },
        radius: 'max',
    };
};
</script>
```

In the above code, we use the `RESIZE_TYPES.FILL` enum from the `@cld-apis/utils` package to crop and resize the image without distortion to a square of 200x200 pixels. We also set the `radius` to `max` for a round thumbnail. 

Once done, we navigate to the `App.vue` component and use the `ImageDemo` component as follows:

```html
<template>
    <ImageDemo
        src="<your-cloudinary-image-url>"
        alt="An example image"
    />
</template>
```

The `src` should be a Cloudinary-based delivery URL, which can be copied in the Cloudinary Console as seen in the screenshot:

<img src="https://res.cloudinary.com/mayashavin/image/upload/q_auto,f_auto/v1629659840/articles/cloudinary/copy_url" alt="Copy Cloudinary URL" class="mx-auto" />

After you pass the URL to `ImageDemo`, you will see the image displayed as in the following example screenshot:

<img src="https://res.cloudinary.com/mayashavin/image/upload/q_auto,f_auto/v1629659840/articles/cloudinary/original_thumbnail" alt="Cloudinary demo" class="mx-auto" />

And upon clicking on the button `Create round thumbnail`, our example image will now become:

<img src="https://res.cloudinary.com/mayashavin/image/upload/q_auto,f_auto/v1629659840/articles/cloudinary/round_thumbnail" alt="Cloudinary demo" class="mx-auto" />

And that's it! We now have a simple demo of using Cloudinary as a composable to transform our image dynamically.

## Summary

In this article, we have learned how to create a composable for transforming images dynamically with Cloudinary using the `cloudinary-build-url` package. We also build a small demo to showcase the composable in action.

What's next? How about being more creative and playing with your image transformations using the [official Cloudinary documentation](https://cloudinary.com/documentation/image_transformations) and the [Cloudinary Build URL package](https://cloudinary-build-url.netlify.app/transformations/basic)? I will love to see what you come up with!

👉 _Learn about Vue with my new book [Learning Vue](https://www.oreilly.com/library/view/learning-vue/9781492098812/). The early release is available now!_

👉 _If you'd like to catch up with me sometimes, follow me on [Twitter](https://twitter.com/MayaShavin) | [Facebook](https://www.facebook.com/mayashavin/) | [Threads](https://www.threads.net/@mayashavin)._

Like this post or find it helpful? Share it 👇🏼 😉