Skip to content

Image and Video tags

In web development, there are many ways to render elements. However, within Terra, we standardize our approach so all team members follow a consistent structure and methodology. This consistency enhances:

  • Maintainability
  • Readability
  • Scalability
  • Overall quality across projects

Typically, when adding images, we might use a simple <img src="asset.jpg" /> tag, which works well for many cases.

However, to achieve perfect rendering, performance optimization, and visual stability, additional attributes are essential.

At Terra, images must follow specific structural and performance rules.

We always include the image’s width and height directly in the HTML. This:

  • Improves performance
  • Prevents layout shifts (CLS)
  • Stabilizes lazy loading behavior

Additionally, we set a dynamic aspect-ratio, unless one of the following conditions applies in the images:

  • The aspect ratio is already defined in CSS.
  • The image has a fixed height in CSS.
  • The image is positioned absolutely and does not affect surrounding layout.

We use the srcset attribute to prevent loading unnecessarily large images on smaller devices.

srcset allows us to provide multiple versions of the same image at different widths (e.g., 580w, 1024w, 2000w). The browser then selects the most appropriate file based on:

  • The viewport size
  • The device pixel ratio (Retina, 2x, etc.)
  • The space the image occupies in the layout (defined by sizes)

In simple terms:

  • srcset → defines the available image versions
  • sizes → tells the browser how large the image will render in the layout

Together, they allow the browser to choose the smallest possible image that still looks sharp.

Both WordPress and Sanity provide built-in tools to manage responsive images.

When adding an image, you only need to determine its expected width in layout and define it in the sizes attribute.

Examples:

  • Full-width image:

    sizes="100vw"
  • 50% width on desktop, 100% on ≤580px:

    sizes="(min-width: 581px) 50vw, 100vw"
  • Fixed 100px icon:

    sizes="100px"

The sizes attribute:

  • Can include media queries
  • Can use any unit (vw, px, etc.)
  • Must clearly reflect real layout behavior

This ensures optimal image loading and consistent layout rendering.

To maximize performance, we prioritize formats in this order:

  1. WebP → Best compression and smallest file sizes
  2. JPG → If WebP is not supported or available
  3. PNG → Only when necessary
<?php
render_wp_image([
'image' => $image,
'sizes' => '(min-width: 1025px) 33vw, (min-width: 581px) 50vw, 95vw',
'class' => 'c--media-a',
'isLazy' => true,
'showAspectRatio' => true,
'decoding' => 'async',
'fetchPriority' => 'auto',
'addFigcaption' => false,
]);
?>
<Asset
payload={{
type: "Image",
url: "https://example.com/image.jpg",
sizes: "(max-width: 600px) 480px, 800px",
alt: "Example image",
customClass: "image-class",
}}
/>
<template>
<Asset :payload="imagePayload" />
</template>
<script setup>
const imagePayload = {
type: 'Image',
url: 'https://example.com/image.png',
alt: 'Example Image',
customClass: 'image-class',
attributes: {
width: 800,
height: 600,
},
};
</script>
<img alt="Image alt" src="imageSrc.webp" srcset="imageSrc.webp 2000w, imageSrc.webp?w=1300 1300w, imageSrc.webp?w=1024 1024w, imageSrc.webp?w=810 810w, imageSrc.webp?w=580 580w" width="2000" height="500" sizes="(max-width: 580px) 95vw, (max-width: 1024px) 50vw, 33vw" class="c--media-a g--lazy-01" style="aspect-ratio: 2000 / 500;" decoding="async" fetchpriority="auto">

Videos are heavy assets. Our goal is to: Avoid loading them on initial page load, load them only when needed and reduce unnecessary resource consumption

We typically use Boostify so videos:

  • Load only when scrolled into view (autoplay)
  • Load only when clicked (non-autoplay)
  • Do not block initial rendering

A Boostify video setup only requires a <div> element; when the video needs to load, the <video> element is dynamically created within that div.

  • In HTML
<div class="c--video-a js--boostify-player" data-video-src="video.mp4"></div>

In this setup, data-video-src holds the video file source, and the video will only load when necessary, contributing to improved page performance.

  • In JavaScript

To use Boostify, you’ll need to import the Boostify package and include some JavaScript code, like this:

document.querySelectorAll('.js--boostify-player').forEach(element => {
bstf.videoPlayer({
url: {
mp4: getAttribute('data-video-src'),
},
attributes: {
class: "c--video-a__media",
loop: false,
muted: false,
controls: true,
autoplay: true,
},
appendTo: element,
});
})
  • Rendered Output
<div class="c--video-a js--boostify-player" data-video-src="video.mp4">
<video class="c--video-a__media" controls autoplay playsinline="true">
<source src="video.mp4" type="video/mp4">
</video>
</div>
  • Example: Astro
<Asset
payload={{
type: "Video",
video_type: 'file', //file example
video_file: '/asset/video/placeholder.mp4',
hasPosterImg: true,
poster_url: 'https://example.com/image.jpg'
customClass: 'video-class',
isBoostify: true,
}}
/>
  • Example: Vue
<template>
<Asset :payload="videoPayload" />
</template>
<script setup>
const videoPayload = {
type: 'Video',
video_type: 'url', //embed example
video_url: 'https://www.youtube.com/watch?v=example',
customClass: 'video-class',
isBoostify: true, //boostify enabled
};
</script>

Native <video> Tag (When Boostify Is Not Possible)

Section titled “Native <video> Tag (When Boostify Is Not Possible)”

Sometimes we cannot use Boostify (e.g., hero background videos). When embedding directly with <video>, autoplay requires:

  • muted
  • playsinline
  • poster

Example:

<div class="c--video-a">
<video class="c--video-a__media" poster="video-poster.webp" autoplay loop playsinline muted>
<source src="video.mp4" type="video/mp4">
</video>
</div>

Knowledge Check

Test your understanding of this section

Loading questions...