Alistair Shepherd - Front-End Developer
SeriesEight
<img
src="/our-image.jpg"
alt="our image"
>
Source jpeg - 300kB
Webp - 18kB 🙂
JPEG XL* - 12kB 😍
Optimised jpeg - 30kB 😮
AVIF - 14kB 😀
<picture>
<source
media="(-webkit-min-device-pixel-ratio: 1.5)"
srcset="https://example.com/images/our-image_width1200_quality40.jpg 1200w"
sizes="...">
<img
srcset="https://example.com/images/our-image_width600_quality80.jpg 600w"
sizes="..."
alt="">
</picture>
<picture>
<source
type="image/avif"
media="(-webkit-min-device-pixel-ratio: 1.5)"
srcset="/assets/image/otter-300-q40.avif 300w, /assets/image/otter-450-q40.avif 450w, /assets/image/otter-600-q40.avif 600w,
/assets/image/otter-800-q40.avif 800w, /assets/image/otter-1000-q40.avif 1000w, /assets/image/otter-1200-q40.avif 1200"
sizes="(min-width: 87rem) 40rem, (min-width: 768px) calc(50vw - 4rem), calc(100vw - 2rem)">
<source
type="image/avif"
srcset="/assets/image/otter-300-q80.avif 300w, /assets/image/otter-450-q80.avif 450w, /assets/image/otter-600-q80.avif 600w,
/assets/image/otter-800-q80.avif 800w, /assets/image/otter-1000-q80.avif 1000w, /assets/image/otter-1200-q80.avif 1200"
sizes="(min-width: 87rem) 40rem, (min-width: 768px) calc(50vw - 4rem), calc(100vw - 2rem)">
<source
type="image/webp"
media="(-webkit-min-device-pixel-ratio: 1.5)"
srcset="/assets/image/otter-300-q40.webp 300w, /assets/image/otter-450-q40.webp 450w, /assets/image/otter-600-q40.webp 600w,
/assets/image/otter-800-q40.webp 800w, /assets/image/otter-1000-q40.webp 1000w, /assets/image/otter-1200-q40.webp 1200"
sizes="(min-width: 87rem) 40rem, (min-width: 768px) calc(50vw - 4rem), calc(100vw - 2rem)">
<source
type="image/webp"
srcset="/assets/image/otter-300-q80.webp 300w, /assets/image/otter-450-q80.webp 450w, /assets/image/otter-600-q80.webp 600w,
/assets/image/otter-800-q80.webp 800w, /assets/image/otter-1000-q80.webp 1000w, /assets/image/otter-1200-q80.webp 1200"
sizes="(min-width: 87rem) 40rem, (min-width: 768px) calc(50vw - 4rem), calc(100vw - 2rem)">
<source
media="(-webkit-min-device-pixel-ratio: 1.5)"
srcset="/assets/image/otter-300-q40.jpg 300w, /assets/image/otter-450-q40.jpg 450w, /assets/image/otter-600-q40.jpg 600w,
/assets/image/otter-800-q40.jpg 800w, /assets/image/otter-1000-q40.jpg 1000w, /assets/image/otter-1200-q40.jpg 1200"
sizes="(min-width: 87rem) 40rem, (min-width: 768px) calc(50vw - 4rem), calc(100vw - 2rem)">
<img
alt="otter standing on a log looking out majestically"
src="/assets/image/otter-1200-q80.jpg"
srcset="/assets/image/otter-300-q80.jpg 300w, /assets/image/otter-450-q80.jpg 450w,
/assets/image/otter-600-q80.jpg 600w, /assets/image/otter-800-q80.jpg 800w,
/assets/image/otter-1000-q80.jpg 1000w, /assets/image/otter-1200-q80.jpg 1200"
sizes="(min-width: 87rem) 40rem, (min-width: 768px) calc(50vw - 4rem), calc(100vw - 2rem)"
width="1200"
height="784"
loading="lazy"
decoding="async"
>
</picture>
> eleventy-base-blog@5.0.2 build
> eleventy
[...]
Copied 957 files / Wrote 19 files in 188.07 seconds (9898.4ms each, v0.12.1)
3 minutes, 8 seconds!
> eleventy-base-blog@5.0.2 build
> eleventy
[...]
Copied 2 files / Wrote 19 files in 0.59 seconds (31.1ms each, v0.12.1)
✅
✅
❎
❎
and many more
User's browser
Server
HTML
CSS
JS
Images
Images
User's browser
Server
HTML
CSS
JS
Images
Image CDN
Optimised
Images
Source
https://example.imagecdn.com
https://example.imagecdn.com
/https://oursite.com
/images/example.jpg
https://example.imagecdn.com
/images/example.jpg
https://example.imagecdn.com
/images/example.jpg
?w=600&h=400
https://example.imagecdn.com/fetch
/images/example.jpg
/w_600,h_400
.../w_300,h_600,c_thumb,g_auto/...
Tall banner with automatic gravity
.../w_1000,h_400,c_fill,g_north/...
Wide banner anchored to top
<picture>
<source
media="(-webkit-min-device-pixel-ratio: 1.5)"
srcset="
https://example.imagecdn.com/images/w_300,q_40/otter.jpg 300w,
https://example.imagecdn.com/images/w_450,q_40/otter.jpg 450w,
https://example.imagecdn.com/images/w_600,q_40/otter.jpg 600w,
https://example.imagecdn.com/images/w_800,q_40/otter.jpg 800w,
https://example.imagecdn.com/images/w_1000,q_40/otter.jpg 1000w,
https://example.imagecdn.com/images/w_1200,q_40/otter.jpg 1200w"
sizes="(min-width: 87rem) 40rem, (min-width: 768px) calc(50vw - 4rem), calc(100vw - 2rem)">
<img
alt="otter standing on a log looking out majestically"
src="https://example.imagecdn.com/images/w_800,q_80/otter.jpg"
srcset="
https://example.imagecdn.com/images/w_300,q_80/otter.jpg 300w,
https://example.imagecdn.com/images/w_450,q_80/otter.jpg 450w,
https://example.imagecdn.com/images/w_600,q_80/otter.jpg 600w,
https://example.imagecdn.com/images/w_800,q_80/otter.jpg 800w,
https://example.imagecdn.com/images/w_1000,q_80/otter.jpg 1000w,
https://example.imagecdn.com/images/w_1200,q_80/otter.jpg 1200w"
sizes="(min-width: 87rem) 40rem, (min-width: 768px) calc(50vw - 4rem), calc(100vw - 2rem)"
width="1200"
height="784"
loading="lazy"
decoding="async"
>
</picture>
{% image {
name: 'otter.jpg'
alt: 'an otter standing on a log looking majestic'
srcset: [300, 450, 600, 800, 1000, 1200]
sizes: '100vw'
width: 1200
height: 800
loading: 'lazy'
class: 'image-class mb-xl'
} %}
accud.io/imagecdn-11ty
import Image from 'components/Image'
export default function () {
return (
<Image
src="otter.jpg"
alt="an otter standing on a log looking majestic"
srcset={[300, 450, 600, 800, 1000, 1200]}
sizes="100vw"
width="1200"
height="800"
loading="lazy"
className="image-class mb-xl"
/>
)
}
accud.io/imagecdn-react
Not just Jamstack!
cms.1896gallery.com/wp-content/uploads/2019/08/artwork-img.jpg
1896gallery.com/assets/internal-graphic.jpg
Image
CDN
1896gallery.imagecdn.com/wp/artwork-img.jpg
1896gallery.imagecdn.com/assets/internal-graphic.jpg
For very high performing sites different origin has an impact
Easy solution with Netlify
[[redirects]]
from = '/cdn/:image'
to = 'https://example.imagecdn.com/:image'
status = 200
These slides, my sources and helpful links on image CDNs available at accud.io/jamstack-imagecdns
On twitter and most social media @accudio
My website and blog posts alistairshepherd.uk