In a previous article, we delved into the common challenges of optimizing the Largest Contentful Paint (LCP) for images.
We explored best practices and showed how TwicPics streamlines the implementation of resolution switching through its user-friendly API and a single master image.
While the use of a single source file for all resolutions is already a significant improvement, it's evident that the code implementation is still complex and lengthy. Furthermore, the proposed solution does not optimize the Cumulative Layout Shift and does not allow for Art direction.
In this short tutorial, we will see how TwicPics Components (more precisely, the TwicPicture
component) makes it easy to address all these issues.
The solution makes the most sense in a framework that handles SSR, and we have chosen to create an example of its usage with Nuxt 3. Of course, you are free to use the framework of your choice.
This tutorial requires using Node.js v18.0.0+ and TwicPics Components 0.27.0+.
Scaffold your new Nuxt 3 Project
You can skip this part if you want to integrate TwicPics Components into an existing Nuxt 3 project.
Setup
# creates a new starter project
npx nuxi@latest init <project-name>
Pick your preferred dependency manager (we chose yarn
) and wait for your new project to be created.
You can now cd
into your new project directory.
Start Nuxt
# starts and open your Nuxt app in development mode
yarn dev --open
If all goes well, Nuxt
should now be serving your project on http://localhost:3000/ and you should see something like this:
If you get stuck at any point, you can refer to the Nuxt getting started page.
Install and configure TwicPics Components
Now that Nuxt
is up and running, it is time to add and configure the Nuxt version of the TwicPics components.
In the following, we will use our test domain:
https://demo.twic.pics
If you don't have a TwicPics domain yet, you can easily create your own for free.
Install TwicPics Components
Add the @twicpics/components
package to your project:
# using yarn
yarn add @twicpics/components
Setting-up TwicPics Components
Since TwicPics components for Nuxt3
are packaged as a module, integration and configuration take place in the nuxt.config.ts
file:
// nuxt.config.ts
export default defineNuxtConfig({
// integration
modules: [`@twicpics/components/nuxt3`],
// configuration
twicpics: {
domain: `https://demo.twic.pics`, // set your own domain here
},
});
And that's it. You are now ready to to use TwicPics components in your project.
TwicPicture enters the scene
As a remember, optimizing the display of LCP images, or more generally, critical images in a web project, we need to fill the srcset
attribute of an img
tag with a list of differently-sized versions of the same image. This is called resolution switching.
For example, at the end of the article discussing LCP image optimization, we had this code:
<img
src="https://<your-domain>/<path-to-your-image>?twic=v1/resize=280"
srcset="
https://<your-domain>/<path-to-your-image>?twic=v1/resize=280 280w,
https://<your-domain>/<path-to-your-image>?twic=v1/resize=480 480w,
https://<your-domain>/<path-to-your-image>?twic=v1/resize=560 560w,
https://<your-domain>/<path-to-your-image>?twic=v1/resize=840 840w,
https://<your-domain>/<path-to-your-image>?twic=v1/resize=960 960w,
https://<your-domain>/<path-to-your-image>?twic=v1/resize=1440 1440w
"
/>
To do the same thing with TwicPicture
, all we have to do is:
<TwicPicture src="path-to-your-image"/>
Let's verify this in our project by making the following changes to the app.vue
file using puppy-dressed-as-a-reindeer.jpeg (jpeg
- 6777 x 3812 px - 15.28 MB) as master file image :
// app.vue
<template>
<TwicPicture
src="puppy-dressed-as-a-reindeer.jpeg"
/>
</template>
Here is the generated code:
<img
alt=""
width="1536"
height="1536"
loading="lazy"
srcset="
https://demo.twic.pics/puppy-dressed-as-a-reindeer.jpeg?twic=v1/cover=3072x3072 3072w,
https://demo.twic.pics/puppy-dressed-as-a-reindeer.jpeg?twic=v1/cover=2560x2560 2560w,
https://demo.twic.pics/puppy-dressed-as-a-reindeer.jpeg?twic=v1/cover=2048x2048 2048w,
https://demo.twic.pics/puppy-dressed-as-a-reindeer.jpeg?twic=v1/cover=1536x1536 1536w,
https://demo.twic.pics/puppy-dressed-as-a-reindeer.jpeg?twic=v1/cover=1280x1280 1280w,
https://demo.twic.pics/puppy-dressed-as-a-reindeer.jpeg?twic=v1/cover=1024x1024 1024w,
https://demo.twic.pics/puppy-dressed-as-a-reindeer.jpeg?twic=v1/cover=768x768 768w,
https://demo.twic.pics/puppy-dressed-as-a-reindeer.jpeg?twic=v1/cover=640x640 640w,
https://demo.twic.pics/puppy-dressed-as-a-reindeer.jpeg?twic=v1/cover=320x320 320w
"
src="https://demo.twic.pics/puppy-dressed-as-a-reindeer.jpeg?twic=v1/cover=1536x1536"
>
And this is what we should get on a DPR2
device when changing the viewport width from a small to a wide screen:
We obtain various squared variants, ideally compressed (webp
), whose resolution is tailored to both the viewport and the pixel density of the device: resolution switching is fully functional.
To achieve the same result while showcasing an image in 16:9
format, we just need to modify app.vue
as follows:
// app.vue
<template>
<TwicPicture
src="puppy-dressed-as-a-reindeer.jpeg"
ratio="16/9"
/>
</template>
And this is what we get, this time on a DPR1
screen:
TwicPicture
allows the display of images not only with various resolutions but also with different aspect ratios, all from a single master file.
You can find the complete list of TwicPicture
properties here. Later on we'll explore how to leverage additional powerful transformations.
Design Control
Now that we know TwicPicture
allows for managing aspect ratio along with resolution switching, it's time to explore how to implement Art Direction.
Let's reopen our app.vue
file and modify it by configuring the ratio
property following the mobile-first principle:
// app.vue
<!--
This will display a:
- 3/4 variant for small screen with width < 666px (a custom breakpoint is used here)
- squared variant for screen with width ∈ [ 666px, 768px [
- 4/3 variant for screen with width ∈ [ 768px, 1024px [
- 21/9 variant for screen with width >= 1280px
-->
<template>
<TwicPicture
src="components/puppy-dressed-as-a-reindeer.jpeg"
ratio="
3/4
@666 1
@md 4/3
@xl 21/9
"
/>
</template>
Default breakpoint values are customizable during components configuration.
Here is the result on a DPR1
screen:
The result is moderately satisfying: we achieve the desired aspect ratios based on the screen size, but the image is poorly framed in its 21/9 variant. Let's fix the framing by adjusting the focus for that specific breakpoint:
// app.vue
<!--
This will apply:
- default focus transformation for screen with width < 1280px
- focus="top" transformation for screen with width >= 1280px
-->
<template>
<TwicPicture
src="components/puppy-dressed-as-a-reindeer.jpeg"
focus="@xl top"
ratio="
3/4
@666 1
@md 4/3
@xl 21/9
"
/>
</template>
This is the generated code:
<picture>
<source
height="549"
width="1280"
media="(min-width: 1280px)"
srcset="
https://demo.twic.pics/puppy-dressed-as-a-reindeer.jpeg?twic=v1/focus=top/cover=3072x1317 3072w,https://demo.twic.pics/puppy-dressed-as-a-reindeer.jpeg?twic=v1/focus=top/cover=2560x1097 2560w,https://demo.twic.pics/puppy-dressed-as-a-reindeer.jpeg?twic=v1/focus=top/cover=1536x658 1536w,https://demo.twic.pics/puppy-dressed-as-a-reindeer.jpeg?twic=v1/focus=top/cover=1280x549 1280w
"
>
<source
height="576"
width="768"
media="(min-width: 768px)"
srcset="
https://demo.twic.pics/puppy-dressed-as-a-reindeer.jpeg?twic=v1/cover=2048x1536 2048w,
https://demo.twic.pics/puppy-dressed-as-a-reindeer.jpeg?twic=v1/cover=1536x1152 1536w,
https://demo.twic.pics/puppy-dressed-as-a-reindeer.jpeg?twic=v1/cover=1024x768 1024w,
https://demo.twic.pics/puppy-dressed-as-a-reindeer.jpeg?twic=v1/cover=768x576 768w
"
>
<source
height="666"
width="666"
media="(min-width: 666px)"
srcset="
https://demo.twic.pics/puppy-dressed-as-a-reindeer.jpeg?twic=v1/cover=1332x1332 1332w,
https://demo.twic.pics/puppy-dressed-as-a-reindeer.jpeg?twic=v1/cover=666x666 666w
"
>
<img
alt=""
height="888"
width="666"
loading="lazy"
srcset="
https://demo.twic.pics/puppy-dressed-as-a-reindeer.jpeg?twic=v1/cover=1280x1707 1280w,
https://demo.twic.pics/puppy-dressed-as-a-reindeer.jpeg?twic=v1/cover=640x853 640w,
https://demo.twic.pics/puppy-dressed-as-a-reindeer.jpeg?twic=v1/cover=320x427 320w
"
>
</picture>
And this is the result with a fixed focus on wide screen:
Other properties of TwicPicture
are configurable based on breakpoints, allowing simple and great flexibility in the implementation of Art Direction.
Feel free to explore the documentation for a deeper understanding of TwicPicture
and its design control feature.
Handling Best Practices
Don't forget the sizes
So far, we have displayed an image that occupies the entire width of the viewport.
Let's modify our example to restrict the width of our image to a maximum of 1200px
. Like this:
// app.vue
<template>
<div class="container">
<TwicPicture
src="components/puppy-dressed-as-a-reindeer.jpeg"
focus="@xl top"
ratio="
3/4
@666 1
@md 4/3
@xl 21/9
"
sizes="
(max-width: 1200px) 100vw,
1200px
"
/>
</div>
</template>
<style>
.container {
margin: auto;
max-width: 1200px;
}
</style>
You've noticed that we used the sizes
property to inform the browser about the effective size of the image to be displayed for a given viewport width.
Without this property, the browser would have loaded the image corresponding to 100% of the viewport, potentially increasing loading times and impacting the LCP score.
It's an easy trap to fall into, so it's crucial not to forget to assign a value to the sizes
property.
Here is the result, with and without sizes
:
Here, forgetting to assign a value to the sizes
property multiplies the weight of the image by 4!
Improve LCP Resource Load Delay Subpart
In our previous article about Image Element LCP optimization, we discussed the issue of lazy-loading and priority hints.
With TwicPicture
, applying best practices to improve LCP score is as simple as adding the eager
property to our component call: this will effectively disable lazy loading for this image and set fetchpriority
attribute to high
.
// app.vue
<template>
<div class="container">
<TwicPicture
src="components/puppy-dressed-as-a-reindeer.jpeg"
eager
focus="@xl top"
ratio="
3/4
@666 1
@md 4/3
@xl 21/9
"
sizes="
(max-width: 1200px) 100vw,
1200px
"
/>
</div>
</template>
<style>
.container {
margin: auto;
max-width: 1200px;
}
</style>
This time the generated code becomes
<picture>
<source
height="549"
width="1280"
media="(min-width: 1280px)"
srcset="
https://demo.twic.pics/puppy-dressed-as-a-reindeer.jpeg?twic=v1/focus=top/cover=3072x1317 3072w,https://demo.twic.pics/puppy-dressed-as-a-reindeer.jpeg?twic=v1/focus=top/cover=2560x1097 2560w,https://demo.twic.pics/puppy-dressed-as-a-reindeer.jpeg?twic=v1/focus=top/cover=1536x658 1536w,https://demo.twic.pics/puppy-dressed-as-a-reindeer.jpeg?twic=v1/focus=top/cover=1280x549 1280w
"
>
<source
height="576"
width="768"
media="(min-width: 768px)"
srcset="
https://demo.twic.pics/puppy-dressed-as-a-reindeer.jpeg?twic=v1/cover=2048x1536 2048w,
https://demo.twic.pics/puppy-dressed-as-a-reindeer.jpeg?twic=v1/cover=1536x1152 1536w,
https://demo.twic.pics/puppy-dressed-as-a-reindeer.jpeg?twic=v1/cover=1024x768 1024w,
https://demo.twic.pics/puppy-dressed-as-a-reindeer.jpeg?twic=v1/cover=768x576 768w
"
>
<source
height="666"
width="666"
media="(min-width: 666px)"
srcset="
https://demo.twic.pics/puppy-dressed-as-a-reindeer.jpeg?twic=v1/cover=1332x1332 1332w,
https://demo.twic.pics/puppy-dressed-as-a-reindeer.jpeg?twic=v1/cover=666x666 666w
"
>
<img
alt=""
fetchpriority="high"
height="888"
width="666"
loading="eager"
sizes="
(max-width: 1200px) 100vw,
1200px
"
srcset="
https://demo.twic.pics/puppy-dressed-as-a-reindeer.jpeg?twic=v1/cover=1280x1707 1280w,
https://demo.twic.pics/puppy-dressed-as-a-reindeer.jpeg?twic=v1/cover=640x853 640w,
https://demo.twic.pics/puppy-dressed-as-a-reindeer.jpeg?twic=v1/cover=320x427 320w
"
>
</picture>
The Trial by Fire
It's time to check the LCP score of a web page using TwicPicture
.
To do this, we've created a small demonstration site where, in addition to TwicPicture
which handles the LCP image, we use TwicImg to display content images.
Here is the result of the performance audit with Chrome's Lighthouse:
Our page achieves a performance score of 100% with a LCP metric equal to 0.5 s.
Significantly the CLS metric is zero as TwicPics Components, by default, seamlessly optimize Cumulative Layout Shift.
To go further
Suppose the background color of the main image isn't exactly right. Or the protagonist's orientation isn't correct.
Should we take another photo?
Fortunately, the answer is no. TwicPics API and its transformations lets you make these adjustments on the fly.
For example, to change the background color to blue #145480
and orient the puppy's head on the other side, simply use the following transformation background=remove+145480/flip=x
.
To apply it to our main image, simply modify the app.vue file by adding the preTransform
property as follows:
// app.vue
...
<TwicPicture
src="components/puppy-dressed-as-a-reindeer.jpeg"
eager
focus="@xl top"
preTransform="background=remove+145480/flip=x"
ratio="
3/4
@666 1
@md 4/3
@xl 21/9
"
sizes="
(max-width: 1200px) 100vw,
1200px
"
/>
...
And that's it:
If you want to learn more about the versatile options offered by TwicPicture
, feel free to explore its properties.
Conclusion
The proper display of a critical image on a web page involves the use of picture
, source
, and img
tags. The associated code is often verbose, time-consuming to develop, and may lead to poor results.
TwicPics Components, particularly the TwicPicture
component, stands out as a robust and efficient solution for optimizing the Largest Contentful Paint (LCP) in web projects, with best practices out of the box, including Cumulative Layout Shift (CLS) optimization.
Whether the objective is to achieve optimal performance or manage design control, TwicPicture
comprehensively and flexibly addresses these challenges, offering a streamlined approach to handling critical images. Additionally, it provides the capability to leverage the power of TwicPics: perfectly compressed images, rich and scalable transformations, and the benefits of its worldwide CDN proximity.
In this tutorial, we used Nuxt3 to assess the ease of use and effectiveness of TwicPicture
. The source code for the test project is at your disposal, allowing you to conduct your own experiments freely. Feel free to replicate the same in the framework of your choice.
TwicPics Components are both free and open-source. You can test them for yourself without any cost. The only requirement is to have a TwicPics account, which you can easily create for free by signing up here.
Top comments (0)