Robust optimization and delivery on hosted images

Teri Eyenike

Reusability is at the heart of web development, using hosted images across multiple applications and websites. The need to transform, optimize, and deliver a version of that image with each application may arise.

This post discusses applying robust transformations and optimizations on hosted images in a Next.js website. Cloudinary, which we will employ, provides a feature to manage this and a suite of image enhancements and optimizations.

Images used in this project

Sandbox

This post is completed in a CodeSandbox. Fork it to get started quickly.

Check out the source code on Github.

Prerequisites

  • Node for installing dependencies
  • Knowledge of Next.js
  • A text/code editor

Setting up the application

We create a Next.js application using the following command:

npx create-next-app image-optimizations && cd image-optimizations

Next, we install the Cloudinary packages using yarn.

1yarn add @cloudinary/url-gen @cloudinary/react
2
3# or
4
5npm i @cloudinary/url-gen @cloudinary/react
  • @cloudinary/url-gen: Includes particular components and directives for easy embedding of assets in our app
  • @cloudinary/react: used to render images on our site

Next, let’s install the classnames package, a JavaScript utility for conditionally joining classNames together in writing the CSS modules for styling.

1yarn add classnames
2
3# or
4
5npm i classnames

Finally, we start a local development server with the command.

1yarn dev
2
3# or
4
5npm run dev

Using Fetch URL

We will use the fetch-url feature of Cloudinary to deliver the hosted image.

Before adding transformations to any component in our project, we create a file in the component directory that renders the transformed image.

In the component/Marathoner.js file we create, we include the following code to display an image using the fetch URL, prefixed to the URL of the image.

component/Marathoner.js

1import { Cloudinary } from "@cloudinary/url-gen";
2 import { AdvancedImage } from "@cloudinary/react";
3
4 import styles from "../styles/Home.module.css";
5
6 export default function Marathoner() {
7 const cld = new Cloudinary({
8 cloud: {
9 cloudName: "demo"
10 }
11 });
12 const myImage = cld
13 .image("https://reefit.netlify.app/img/eloho.jpg")
14 .setDeliveryType("fetch");
15
16 return (
17 <div>
18 <AdvancedImage
19 className={styles.img}
20 cldImg={myImage}
21 />
22 </div>
23 );
24 }

The above snippet creates and configures a new Cloudinary instance using a public identifier, cloudName. You can get this from your Cloudinary dashboard after you create an account.

Using the Cloudinary instance created, we defined a new image pointing to a publicly hosted photo, and we set the delivery type to fetch. Lastly, we rendered the transformed image using the AdvancedImage component.

We create a file called Home.module.css in the styles folder having the following content.

styles/Home.module.css

1@import url("https://fonts.googleapis.com/css?family=Poppins:200,300,400,500,600,700,800,900&display=swap");
2
3 .main {
4 font-family: "Poppins", sans-serif;
5 }
6 .align {
7 text-align: center;
8 }
9 .img {
10 max-width: 100%;
11 display: block;
12 }
13 .container {
14 max-width: 75rem;
15 padding-inline: 2em;
16 margin: auto;
17 width: 85%;
18 }
19 .space {
20 margin-bottom: 2em;
21 }

Applying transformations to a hosted image

After delivering the image, we want to add transformations to it. Using @cloudinary/url-gen, we can import specific Cloudinary transformation actions and qualifiers. These are then used to build a complete image URL with transformations.

Alternatively, we could apply on-the-fly transformations to the image by modifying the resulting image URL.

Still in the component/Marathoner.js, include the following code to transform the image.

component/Marathoner.js

1import { Cloudinary } from "@cloudinary/url-gen";
2 import { AdvancedImage } from "@cloudinary/react";
3 import { Transformation } from "@cloudinary/url-gen";
4
5 // import required actions and qualifiers
6 import { thumbnail, scale } from "@cloudinary/url-gen/actions/resize";
7 import { byRadius } from "@cloudinary/url-gen/actions/roundCorners";
8 import { grayscale } from "@cloudinary/url-gen/actions/effect";
9 import { source } from "@cloudinary/url-gen/actions/overlay";
10 import { FocusOn } from "@cloudinary/url-gen/qualifiers/focusOn";
11 import { Position } from "@cloudinary/url-gen/qualifiers/position";
12 import { focusOn } from "@cloudinary/url-gen/qualifiers/gravity";
13 import { compass } from "@cloudinary/url-gen/qualifiers/gravity";
14 import { image } from "@cloudinary/url-gen/qualifiers/source";
15
16 import styles from "../styles/Home.module.css";
17
18 // javascript utility for classnames
19 import cls from "classnames";
20 export default function Marathoner() {
21 const cld = new Cloudinary({
22 cloud: {
23 cloudName: "demo"
24 }
25 });
26 const myImage = cld
27 .image("https://reefit.netlify.app/img/eloho.jpg")
28 .setDeliveryType("fetch");
29
30 myImage
31 .resize(thumbnail().width(250).height(250).gravity(focusOn(FocusOn.face())))
32 .roundCorners(byRadius(20))
33 .effect(grayscale())
34 .overlay(
35 source(
36 image("cloudinary_icon_blue").transformation(
37 new Transformation().resize(scale(50))
38 )
39 ).position(
40 new Position().gravity(compass("south_west")).offsetX(10).offsetY(5)
41 )
42 );
43 return (
44 <div>
45 <AdvancedImage
46 className={cls(styles.img, styles.space)}
47 cldImg={myImage}
48 />
49 </div>
50 );
51 }

The code above applies the following transformations:

  • The image is cropped to a 250 x 250 thumbnail using face-detection gravity to determine the crop's location automatically
  • Round the corners of the image with a radius of 20px
  • Applied to the image is the grayscale effect
  • Applied to the image is the Cloudinary logo on the southwest corner of the photo and scaled-down to 50px width of the original size with an offset from the edge of the picture in the X and Y direction

Applying optimizations to a hosted image

Like the image transformation steps, we’ll add a transformation effect to an image, along with format and quality optimizations.

Next, we create a new file called Athlete.js in the component directory with the following content:

component/Athlete.js

1import { Cloudinary } from "@cloudinary/url-gen";
2 import { AdvancedImage } from "@cloudinary/react";
3
4 // import required actions
5 import { vectorize } from "@cloudinary/url-gen/actions/effect";
6 import styles from "../styles/Home.module.css";
7 export default function Athlete() {
8 const cld = new Cloudinary({
9 cloud: {
10 cloudName: "demo"
11 }
12 });
13 const athlete = cld
14 .image("https://images.pexels.com/photos/6354732/pexels-photo-6354732.jpeg")
15 .setDeliveryType("fetch");
16
17 athlete.effect(vectorize()).format("auto").quality("auto");
18 return (
19 <div>
20 <AdvancedImage className={styles.img} cldImg={athlete} />
21 </div>
22 );
23 }

The resulting image should look like this:

Conclusion

This post discussed modifying and reusing images hosted publicly on the internet by applying Cloudinary transformation and optimization parameters.

Further Reading

Teri Eyenike

Software Developer

Teri Eyenike is a software developer interested in making things simple and usable.