This content originally appeared on DEV Community and was authored by Raheel Shan
For years, i have use <img/>
tag for displaying images on web pages. It is simple, straightforward and does the job well. However as i have shifted my focus on performance, SEO and UX, i realized the limitations of <img/>
tag. After getting familiar with <picture/>
tag and doing some experiments, i made a permanent switch and haven’t looked back.
In this post, i will explain why <picture/>
is the better choice, how it solves major image-related issues, and the best practices to use it effectively.
The limitations of <img/>
The <img/>
tag has been a web standard for decades, but with the latest web standards, it comes with a few drawbacks. Lets explore them one by one:
1. Lacks support for modern Web Formats like Webp
Modern image formats like WebP and AVIF offer better compression and quality than PNG or JPEG. However, using <img/>
alone means:
- Either you serve only WebP by risking incompatibility on older browsers
- Or you stick to traditional formats by sacrificing performance
2. No fallback handling
If an image fails to load due to a broken link, <img/>
doesn’t automatically switch to another version. The only workaround is using JavaScript or an onerror
event, which can be unreliable. Because if fallback image is not found javascript will take the web page into an infinite loop.
3. Potential for Layout Shift (CLS Issues)
Without explicitly setting width and height on <img />
tag causes cumulative layout shift (CLS) decreasing page load time which is a bad UX where content jumps when images load late.
4. Limited Responsiveness & Styling
Although <img/>
tag allows setting width and height, these values are static unless overridden by CSS.
How <picture>
Solves These Issues
The <picture/>
tag provides a more flexible, SEO-friendly, and performance-optimized way to load images.
1. Better Format Handling with <source/>
With <picture>
, you can specify multiple image formats and let the browser automatically choose the best one:
<picture>
<source srcset="image.webp" type="image/webp">
<img src="image.jpg" alt="Example Image">
</picture>
- Browsers that support WebP will load
image.webp
- Others will fall back to
image.jpg.
- No JavaScript needed!
2. Fallback Support for Broken Images
Instead of using onerror
, <picture>
provides an elegant fallback:
<picture>
<source srcset="/url/to/image/path.webp" type="image/webp">
<img id="logo" src="/url/to/noimage/path.webp" alt="Logo Image">
</picture>
- If source image is missing, the fallback is loaded automatically.
3. Preventing Layout Shift with Proper Dimensions
To avoid CLS, always define width
and height
inside <img>
:
<picture>
<source srcset="image.webp" type="image/webp">
<img id="logo" src="fallback.jpg" width="120" height="100" class="w-[120px] h-[100px] object-cover">
</picture>
- Browsers can reserve space before loading images, preventing shifts.
- Tailwind classes (w-[120px] h-[100px]) ensure flexibility—overriding static width/height when needed.
- For responsiveness, you can replace w-[120px] h-[100px] with w-auto max-w-full.
Tip: Use aspect-ratio
for better image sizing. The below example ensures a consistent aspect ratio and prevents layout shifts.
<picture>
<source srcset="feature-image.webp" type="image/webp">
<img class="w-full aspect-[16/9] object-cover" src="image.webp" alt="Example">
</picture>
Tip: Use a Darker Background for Contrast. Applying bg-gray-800
or a similar dark background ensures images stand out on light themes. This makes the UX feel smoother by preventing blank spaces before images appear.
<picture>
<source srcset="feature-image.webp" type="image/webp">
<img class="w-32 h-32 bg-gray-800" src="image.webp" alt="Example">
</picture>
4. Lazy-Loading for Performance
To improve performance and reduce initial page load time, always use loading="lazy"
:
<picture>
<source srcset="image.webp" type="image/webp">
<img id="logo" src="fallback.jpg" width="120" height="100" class="w-[120px] h-[100px] object-cover" loading="lazy">
</picture>
- Speeds up page load times by loading images only when they are about to enter the viewport.
5. Above-the-Fold vs. Below-the-Fold Images
Above-the-Fold Image (Eager Loading)
Above-the-fold images or images that are visible to viewport on page load should load immediately to ensure a good user experience:
<picture>
<source srcset="hero-image.webp" type="image/webp">
<img src="hero-image.jpg" alt="Hero Section Image" width="800" height="400" class="w-full h-auto object-cover" loading="eager">
</picture>
Below-the-Fold Image (Lazy Loading)
For images lower on the page, use loading=”lazy” to defer loading until needed:
<picture>
<source srcset="feature-image.webp" type="image/webp">
<img src="feature-image.jpg" alt="Feature Image" width="600" height="300" class="w-full h-auto object-cover" loading="lazy">
</picture>
6. JavaScript Image Previews
Since the <img>
inside <picture>
can have an id
, JavaScript can easily update it for image previews:
document.getElementById("logo").src = URL.createObjectURL(fileInput.files[0]);
- Allows previewing images before uploading them without breaking the fallback structure.
Lesson Crux
Here is the resultant component.
<picture>
<source srcset="image.webp" type="image/webp">
<source srcset="image.avif" type="image/avif">
<source srcset="image.jpg" type="image/jpg">
<img src="noimage.webp" id="logo" alt="Logo Image" width="600" height="300" class="w-full h-auto object-cover" loading="lazy">
</picture>
<input name="logo" onchange="loadFile(event,'logo')" type="file" >
var loadFile = function(event, id) {
var input = event.target;
var file = input.files[0];
if (!file) return; // Stop if no file is selected
var output = document.getElementById(id);
var source = output.closest('picture').querySelector('source');
var objectURL = URL.createObjectURL(file);
// Update the image preview
output.src = objectURL;
// Also update the <source> to ensure proper loading
source.srcset = objectURL;
output.onload = function() {
URL.revokeObjectURL(objectURL); // Free memory
};
};
Summary: Why <picture/>
is the better
Switching from <img/>
to <picture/>
is one of the best decisions for any of my projects. Here’s why I always prefer <picture/>
now:
- SEO optimized images with WebP and Avif support.
- Fallback image support without the need of javascript.
- Prevents cumulative layout shifts and override for responsive images.
- Lazy load for faster loading web pages.
- Image preview available with javascript.
If you’re still using <img/>
alone, I highly recommend upgrading to . It’s a small change with a huge impact!
This content originally appeared on DEV Community and was authored by Raheel Shan