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 281w14 "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:
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 281w19 "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 281w15 "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 100vw10 "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 281w23 "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<div2 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 @endforeach8 // ...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<div2 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 @endforeach8 // ...9</div>