
Here’s a live example for you:
<img src="/sites/default/files/2025-01/my-image.jpg" loading="lazy" alt="Example Image">
For Slick:
What is Lazy Loading?
Take a look at Specbee’s blog listing page: https://www.specbee.com/blogs
If you need more customization and flexibility in addition to simply lazily loading your images, we’ve got you covered.
Lazy loading waits rather than immediately loading every single image, video, and iframe onto the page. As the user scrolls down, it retrieves the remaining content after loading what is now visible on the screen.
Drupal’s built-in lazy loading feature may not always work, such as when you’re using a JS framework or AJAX to generate images dynamically. In certain situations, a simple, contemporary method of determining when anything enters the viewport is to use the Intersection Observer API:
<img
loading="lazy"
width="830"
height="835"
src="{{ file_url(row.content['#row']._entity|translation.field_image.entity.uri.value) }}"
alt="{{ row.content['#row']._entity.field_image.alt }}"
/>
var swiper = new Swiper('.swiper-container', {
on: {
slideChangeTransitionStart: function () {
var activeSlide = this.slides[this.activeIndex];
$(activeSlide).find('img[data-src]').each(function() {
$(this).attr('src', $(this).data('src')).removeAttr('data-src');
});
}
}
});
<div class="lazy-bg" data-bg="{{ file_url(content.field_banner_image.0['#item'].entity.uri.value) }}"></div>How to fix it? You can use
data-srcfor all other images and load the first one normally:
Along with the many performance-enhancing features built into Drupal, lazy loading is one of the most effective (and most noticeable). It greatly improves the one thing your users feel immediately when they land on your site – and that is how fast the page loads.When built-in isn’t good enough for your site
Like the name suggests, lazy loading will not load content until someone needs it in their viewport.
Adding it manually in Twig
If you want to make sure your website’s performance stays invisible, but keeps delivering, you might want to reach out to Specbee’s Drupal experts who can implement customized and cost-effective Drupal solutions.
composer require drupal/image_lazyloader
drush en image_lazyloaderImagine ten huge photographs in a blog article. Without lazy loading, the page sits there as the browser attempts to grab all ten at once. Lazy loading loads the remaining ones in the background while you read, getting the first couple that are visible. The whole concept is that you’re unlikely to even notice it happening.
Yes, if you’re working with Drupal 9.1 or higher you should know that lazy loading is enabled by default for standard image fields, views, and media entities. Drupal automatically adds the loading=”lazy” to your image tags:Using JavaScript to Lazy Load
{% for item in items %}
<div class="slide">
{% if loop.first %}
<img src="{{ file_url(item.entity.field_image.entity.uri.value) }}" alt="{{ item.entity.field_image.alt }}">
{% else %}
<img data-src="{{ file_url(item.entity.field_image.entity.uri.value) }}" class="lazy-slide" alt="{{ item.entity.field_image.alt }}">
{% endif %}
</div>
{% endfor %}Even in more complex scenarios like background images, AJAX-driven content, or dynamic sliders, the fixes remain lightweight. A small tweak in Twig here, a bit of JavaScript there, and the system continues to scale seamlessly.
(function ($, Drupal) {
Drupal.behaviors.lazyBackground = {
attach: function (context) {
$('.lazy-bg', context).once('lazy-bg').each(function () {
var bg = $(this).data('bg');
$(this).css('background-image', 'url(' + bg + ')');
});
}
};
})(jQuery, Drupal);
One of those infrequent victories where the return is genuine, and the work is minimal, is lazy loading. Faster pages, stronger Core Web Vitals, and a smoother experience are becoming the baseline. And with modern Drupal, you’re already a step ahead.
That’s lazy loading in action. Very subtle, but it makes the entire experience feel much faster and smoother, don’t you think?
Most users won’t notice lazy loading at all. They’ll just feel that your site works better, loads quicker, and responds the way it should.
Image sliders are a problem that I’ve encountered numerous times. Even if only the first slide image is visible, libraries such as Slick, Swiper, or Owl Carousel frequently load all of the slide images up front. That’s a lot of bandwidth spent on a product carousel with a dozen high-resolution banners.
Now, nobody enjoys waiting for a page to load. When you click on a link, you’re already considering pressing the back button if nothing appears right away. The reason why the web page takes so long to load is because the browser is attempting to retrieve dozens of high-resolution images at once, causing serious delays in loading the page.
CSS background images, such as large hero banners and section backgrounds that are set by background-image, is something Drupal does not automatically lazy load. However, a minor Drupal behavior can resolve it:What about those background images?
$('.your-slider').on('beforeChange', function(event, slick, currentSlide, nextSlide){
var $nextSlide = $(slick.$slides[nextSlide]);
$nextSlide.find('img[data-src]').each(function() {
$(this).attr('src', $(this).data('src')).removeAttr('data-src');
});
});That’s where lazy loading comes in. It sounds complex, but it’s actually pretty straightforward. In Drupal 9.1 and above, it’s built right in, so you get the benefits without extra effort. And if you want more control, there are ways to fine-tune how it works. Read the full article to see how you can make the most of it.
If you’re writing custom Twig templates or overriding views, you can add lazy loading yourself. It’s as simple as dropping in the attribute:Final thoughts
The Image Lazyloader contrib Drupal module is worth looking at if you’re using an earlier version of Drupal (pre-9.1) or if you want more advanced capabilities like loading animations or custom placeholder images.
<img data-src="{{ file_url(content.field_image.entity.uri.value) }}" alt="{{ content.field_image.alt }}" />
document.addEventListener("DOMContentLoaded", function() {
const lazyImages = document.querySelectorAll("img[data-src]");
const observer = new IntersectionObserver((entries, observer) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
const img = entry.target;
img.src = img.dataset.src;
img.removeAttribute("data-src");
observer.unobserve(img);
}
});
});lazyImages.forEach(img => observer.observe(img));
});As you scroll, notice how the images don’t load all at once. The ones in your view appear first, and the rest load just in time as you move down the page. Even the next image is quietly fetched in the background right before you reach it.






