Introduction

Why use srcset and sizes?

Let’s start at the beginning. Why even use srcset and sizes? There are two two real advantages:

  • Delivering images that are better suited for the user's device.
  • Improving the website's loading time.

With srcset and sizes it is possible to offer multiple sizes of the same image. The browser does the calculation and chooses the best size to display to the user. Browser support of srcset and sizes is excellent and the fallback is perfect.

The browser does the calculation and chooses the best size to display to the user.

The browser not only takes into account the width of the screen (viewport width), but also the pixel density. Whether a user is viewing your website on a desktop screen with low resolution or a tablet screen with high resolution, the browser chooses the best size. Because you can offer multiple sizes of the same image, you can improve the loading time of your site. It’s no longer needed to serve that big hero image on a small screen. With srcset and sizes you can offer a smaller size which will be used by the browser. This results in a faster website.


Deep Dive into srcset and sizes

Let's start by taking a closer look at how srcset is used in the browser.
Out of the box, Strata will automatically generate and populate the srcset attribute for you.

Example

1{!! $this->get('image')->first() !!}
1<img
2 src="https://res.cloudinary.com/helixsleep/image/upload/v1/samples/imagecon-group?_a=AAAGSAI"
3 srcset="
4 https://res.cloudinary.com/helixsleep/image/upload/c_scale,w_2800/v1/samples/imagecon-group?_a=AAAGSAI 2800w,
5 https://res.cloudinary.com/helixsleep/image/upload/c_scale,w_2168/v1/samples/imagecon-group?_a=AAAGSAI 2168w,
6 https://res.cloudinary.com/helixsleep/image/upload/c_scale,w_1680/v1/samples/imagecon-group?_a=AAAGSAI 1680w,
7 https://res.cloudinary.com/helixsleep/image/upload/c_scale,w_1301/v1/samples/imagecon-group?_a=AAAGSAI 1301w,
8 https://res.cloudinary.com/helixsleep/image/upload/c_scale,w_1007/v1/samples/imagecon-group?_a=AAAGSAI 1007w,
9 https://res.cloudinary.com/helixsleep/image/upload/c_scale,w_780/v1/samples/imagecon-group?_a=AAAGSAI 780w,
10 https://res.cloudinary.com/helixsleep/image/upload/c_scale,w_604/v1/samples/imagecon-group?_a=AAAGSAI 604w,
11 https://res.cloudinary.com/helixsleep/image/upload/c_scale,w_468/v1/samples/imagecon-group?_a=AAAGSAI 468w,
12 https://res.cloudinary.com/helixsleep/image/upload/c_scale,w_362/v1/samples/imagecon-group?_a=AAAGSAI 362w,
13 https://res.cloudinary.com/helixsleep/image/upload/c_scale,w_281/v1/samples/imagecon-group?_a=AAAGSAI 281w
14 "
15>


Based solely on the provided srcset, the browser will pick the src which matches the viewport width. This is great if your image streaches all the way from the left edge of the screen to the right edge. Like in this video:

Edge to edge - Watch video


But in a more realistic scenario, your image would likely be more contained on the page, but the browser still doesn't know anything about how wide we want our image. In this example we have the image inside of a div container with a max width set. So you'll see as we resize the browser again, that bigger and bigger images are loaded, eventhough the rendered image doesn't get any bigger than our defined max width. This is obviously a waste of bandwidth.

1<div class="max-w-lg">
2 {!! $this->get('image')->first() !!}
3</div>

Max width on parent container - Watch Video


To solve for this we can tell the browser which image we want it to use based on the viewport size. This is where the sizes attribute comes in to play. So let's take a look at how we can use this. Take this example

1<div class="max-w-lg">
2 <img
3 sizes="
4 (min-width: 768px) 768px,
5 100vw
6 "
7 src="https://res.cloudinary.com/helixsleep/image/upload/v1/samples/imagecon-group?_a=AAAGSAI"
8 srcset="
9 https://res.cloudinary.com/helixsleep/image/upload/c_scale,w_2800/v1/samples/imagecon-group?_a=AAAGSAI 2800w,
10 https://res.cloudinary.com/helixsleep/image/upload/c_scale,w_2168/v1/samples/imagecon-group?_a=AAAGSAI 2168w,
11 https://res.cloudinary.com/helixsleep/image/upload/c_scale,w_1680/v1/samples/imagecon-group?_a=AAAGSAI 1680w,
12 https://res.cloudinary.com/helixsleep/image/upload/c_scale,w_1301/v1/samples/imagecon-group?_a=AAAGSAI 1301w,
13 https://res.cloudinary.com/helixsleep/image/upload/c_scale,w_1007/v1/samples/imagecon-group?_a=AAAGSAI 1007w,
14 https://res.cloudinary.com/helixsleep/image/upload/c_scale,w_780/v1/samples/imagecon-group?_a=AAAGSAI 780w,
15 https://res.cloudinary.com/helixsleep/image/upload/c_scale,w_604/v1/samples/imagecon-group?_a=AAAGSAI 604w,
16 https://res.cloudinary.com/helixsleep/image/upload/c_scale,w_468/v1/samples/imagecon-group?_a=AAAGSAI 468w,
17 https://res.cloudinary.com/helixsleep/image/upload/c_scale,w_362/v1/samples/imagecon-group?_a=AAAGSAI 362w,
18 https://res.cloudinary.com/helixsleep/image/upload/c_scale,w_281/v1/samples/imagecon-group?_a=AAAGSAI 281w
19 "
20 >
21</div>

This means that if the viewport is a minimum of 768px wide it will look in our srcset to find the entry that is still bigger or equal to the size we asked for. In this case it would be

14https://res.cloudinary.com/helixsleep/image/upload/c_scale,w_780/v1/samples/imagecon-group?_a=AAAGSAI 780w

as we told the browser to give up something for 768px and 780w covers that.

We can even define multiple sizes for various breakpoints.

1<img
2 sizes="(min-width: 1152px) 1152px, (min-width: 768px) 768px, (min-width: 576px) 576px, 100vw"
3 src="https://res.cloudinary.com/helixsleep/image/upload/v1/samples/imagecon-group?_a=AAAGSAI"
4 srcset="
5 https://res.cloudinary.com/helixsleep/image/upload/c_scale,w_2800/v1/samples/imagecon-group?_a=AAAGSAI 2800w,
6 https://res.cloudinary.com/helixsleep/image/upload/c_scale,w_2168/v1/samples/imagecon-group?_a=AAAGSAI 2168w,
7 https://res.cloudinary.com/helixsleep/image/upload/c_scale,w_1680/v1/samples/imagecon-group?_a=AAAGSAI 1680w,
8 https://res.cloudinary.com/helixsleep/image/upload/c_scale,w_1301/v1/samples/imagecon-group?_a=AAAGSAI 1301w,
9 https://res.cloudinary.com/helixsleep/image/upload/c_scale,w_1007/v1/samples/imagecon-group?_a=AAAGSAI 1007w,
10 https://res.cloudinary.com/helixsleep/image/upload/c_scale,w_780/v1/samples/imagecon-group?_a=AAAGSAI 780w,
11 https://res.cloudinary.com/helixsleep/image/upload/c_scale,w_604/v1/samples/imagecon-group?_a=AAAGSAI 604w,
12 https://res.cloudinary.com/helixsleep/image/upload/c_scale,w_468/v1/samples/imagecon-group?_a=AAAGSAI 468w,
13 https://res.cloudinary.com/helixsleep/image/upload/c_scale,w_362/v1/samples/imagecon-group?_a=AAAGSAI 362w,
14 https://res.cloudinary.com/helixsleep/image/upload/c_scale,w_281/v1/samples/imagecon-group?_a=AAAGSAI 281w
15 "
16>

The order of the sizes seems to be important. Has to be in descending order; largest to smallest.


Usage

Strata comes with a default srcset file size based calculator and a default Tailwind class based sizes generator.

Srcset

FileSizeOptimizedWidthCalculator

The FileSizeOptimizedWidthCalculator::class takes three parameters:

  • The filesize of the image in bytes
  • The width of the image
  • The height of the image

Based on those three parameters if calculates the sizes smaller than the previous on by a factor of 0.6 until it reach a minimum threshold.

This is already integrated with the Cloudinary media provider, so all of this functionality comes automatically.

Sizes

TailwindSizesGenerator

Strata provides a Tailwind sizes generator that takes Tailwind classes and generators sizes for you based on the those. Easiest way to get started is to just pass the classes to you image as would normally do, and the generator we transform those into sizes for the browser to understand.

Example

1@foreach($this->get('media') as $media)
2 {!! $media->class('max-w-screen-sm md:max-w-screen-md lg:max-w-screen-lg xl:max-w-screen-xl 2xl:max-w-screen-2xl') !!}
3@endforeach

Output:

1<img
2 class="max-w-screen-sm md:max-w-screen-md lg:max-w-lg xl:max-w-screen-xl 2xl:max-w-screen-2xl"
3 sizes="
4 (min-width: 1536px) 1536px,
5 (min-width: 1280px) 1280px,
6 (min-width: 1024px) 32rem,
7 (min-width: 768px) 768px,
8 (min-width: 100vw) 640px,
9 100vw
10 "
11 src="https://res.cloudinary.com/helixsleep/image/upload/v1/samples/imagecon-group?_a=AAAGSAI"
12 srcset="
13 https://res.cloudinary.com/helixsleep/image/upload/c_scale,w_2800/v1/samples/imagecon-group?_a=AAAGSAI 2800w,
14 https://res.cloudinary.com/helixsleep/image/upload/c_scale,w_2168/v1/samples/imagecon-group?_a=AAAGSAI 2168w,
15 https://res.cloudinary.com/helixsleep/image/upload/c_scale,w_1680/v1/samples/imagecon-group?_a=AAAGSAI 1680w,
16 https://res.cloudinary.com/helixsleep/image/upload/c_scale,w_1301/v1/samples/imagecon-group?_a=AAAGSAI 1301w,
17 https://res.cloudinary.com/helixsleep/image/upload/c_scale,w_1007/v1/samples/imagecon-group?_a=AAAGSAI 1007w,
18 https://res.cloudinary.com/helixsleep/image/upload/c_scale,w_780/v1/samples/imagecon-group?_a=AAAGSAI 780w,
19 https://res.cloudinary.com/helixsleep/image/upload/c_scale,w_604/v1/samples/imagecon-group?_a=AAAGSAI 604w,
20 https://res.cloudinary.com/helixsleep/image/upload/c_scale,w_468/v1/samples/imagecon-group?_a=AAAGSAI 468w,
21 https://res.cloudinary.com/helixsleep/image/upload/c_scale,w_362/v1/samples/imagecon-group?_a=AAAGSAI 362w,
22 https://res.cloudinary.com/helixsleep/image/upload/c_scale,w_281/v1/samples/imagecon-group?_a=AAAGSAI 281w
23 "
24>


Of course, sometimes the width of your image is not coming from the classes on your image but from a parent element somewhere else.

1<div
2 class="mx-auto max-w-screen-sm md:max-w-screen-md lg:max-w-lg xl:max-w-screen-xl 2xl:max-w-screen-2xl text-green"
3>
4 // ...
5 @foreach($this->get('media') as $media)
6 {!! $media !!}
7 @endforeach
8 // ...
9</div>

For this we can use the sizes() method on the image to inform it of which classes it should generate sizes for without actually applying those classes to the image itself.

1<div
2 class="mx-auto max-w-screen-sm md:max-w-screen-md lg:max-w-lg xl:max-w-screen-xl 2xl:max-w-screen-2xl text-green"
3>
4 // ...
5 @foreach($this->get('media') as $media)
6 {!! $media->sizes('max-w-screen-sm md:max-w-screen-md lg:max-w-lg xl:max-w-screen-xl 2xl:max-w-screen-2xl') !!}
7 @endforeach
8 // ...
9</div>