Blog home

components lcp image new feature how to

A Simple Approach to LCP Image Optimization with TwicPics Components

Discover an easy solution to streamline Largest Contentful Paint (LCP) image optimization in your web projects.

A Simple Approach to LCP Image Optimization with TwicPics Components

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:

Nuxt is running.
Nuxt is running.

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 use TwicPics components in your project.

TwicPicture enters the scene

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 the 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:

A Resolution Switching in action.

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:

Resolution Switching for a 16:9 variant.

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:

One single master file with art direction.

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:

A true art direction.

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:

Don't forget the 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:

The Trial by Fire
The Trial by Fire

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:

Using stunning transformations on the fly.

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.