Roots Discourse

How to Use Lozad.js in Sage


Originally published at:

Lozad.js is a lightweight lazy-loading library that’s just 535 bytes minified & gzipped. It is written with an aim to lazy load images, iframes, ads, videos or any other element using the recently added Intersection Observer API with tremendous performance benefits. Install yarn add lozad Import and add JS Open resources/assets/scripts/routes/common.js and add the import…



Excellent - thank you.



Thanks. If you’re also using the srcset attribute your image tag will look like this:

<img class="lozad" data-src="path-to-src.jpg" data-srcset="..." />
1 Like



A few days ago I decided to use lodaz in a wordpress site i’m developping, and while I was asking myself what could make a great placeholder before images load, I tought it would be nice to leverage wordpress thumbnails feature for that.

The idea is to register a low resolution thumbnail size, in my case 64x48 (around 1~2kb per image), and display a blurred version of it before lodaz does its job and replace it with full quality image version. Then fade the blur out. It would make a kind of Medium-style lazy load.

The final result looks like this:

So first register the low res thumbnail size:

add_image_size('low-res', 64, 48, false);

Then, I use a regex filter function to replace original image codes:

function lazy_images($content)
    //-- Change src/srcset to data attributes, and replace img src with its low res version
    $content = preg_replace_callback(
        function ($matches) {
            return "<img$matches[1]$matches[2]" .low_res_image_src($matches[3]) . " " . "data-$matches[2]$matches[3]$matches[4]data-$matches[5]\"$matches[6]\"$matches[7]>";
    // wrap images in a .lazy-wrap div for clean and unblurred borders. Transfer alignment class to wrapping div
    $content = preg_replace('/(<img|<iframe)(.*?)(align\w*)([^>]*>)/i', '<div class="lazy-wrap $3">$1$2$4</div>', $content);

    //-- Add .lazy class to each image that already has a class.
    $content = preg_replace('/(<img|<iframe)(.*?)class=\"(.*?)\"(.*?)>/i', '$1$2class="$3 lazy"$4>', $content);

    //-- Add .lazy class to each image that doesn't already have a class.
    $content = preg_replace('/(<img|<iframe)((?:(?!class=).)*?)>/i', '$1 class="lazy"$2>', $content);
    return $content;

This filter is applied to content, widgets, and thumbnails:

add_filter('the_content', 'lazy_images');
add_filter('widget_text', 'lazy_images');
add_filter('post_thumbnail_html', 'lazy_images'); 

What we need next is the low_res_image_src function that will return the image low res version url:

function low_res_image_src($image_url)
    //Call the get_attachment_id that will return the id based on image url. Here a slight adjustment may be required to get rid of domain in url
    $iid = get_attachment_id(WP_HOME . $image_url);
    return wp_get_attachment_image_src($iid, 'low-res')[0];

function get_attachment_id($url)
    $attachment_id = 0;

    $dir = wp_upload_dir();

    if (false !== strpos($url, $dir['baseurl'] . '/')) { // Is URL in uploads directory?
        $file = basename($url);

        $query_args = array(
            'post_type'   => 'attachment',
            'post_status' => 'inherit',
            'fields'      => 'ids',
            'meta_query'  => array(
                    'value'   => $file,
                    'compare' => 'LIKE',
                    'key'     => '_wp_attachment_metadata',

        $query = new WP_Query($query_args);

        if ($query->have_posts()) {
            foreach ($query->posts as $post_id) {
                $meta = wp_get_attachment_metadata($post_id);

                $original_file       = basename($meta['file']);
                $cropped_image_files = wp_list_pluck($meta['sizes'], 'file');

                if ($original_file === $file || in_array($file, $cropped_image_files)) {
                    $attachment_id = $post_id;

    return $attachment_id;

Add the required CSS (you might want to inline it in your HTML):

.lazy-wrap {
  overflow: hidden;
  width: fit-content;

img.lazy {
  transition: filter 0.3s ease;
  filter: blur(10px);
  //we could also add "transform : scale(1.1)" to avoid white borders around blurred image
} {
  filter: blur(0);

And finally, we can initialize our lodaz script:

 lozad(".lazy", {
         rootMargin: "500px 0px",
         loaded: function (el) {


Should this be modified to work for srcset attributes output by wp_get_attachment_image?

Edit, like:

    if ($attr['src']) {
        $attr['data-src'] = $attr['src'];

    if ($attr['srcset']) {
        $attr['data-srcset'] = $attr['srcset'];
1 Like


Did someone already found a solution to filter the output from Gutenberg?
The filter wp_get_attachment_image_attributes does not seem to work here.



Hi there – Any idea how we can default this to lozad all images?

Unfortunately, the WP produced “wp-block-cover” header images that come straight out of Sage are DIV backgrounds… Do we append a class to this header somehow, or do we just default all images/iframes regardless?

Many thanks!



This is awesome. Is it possible however to edit the regex to ignore any images that already have a data-src attribute? For example I have a Slick slider which is lazy-loading images itself, which are then broken by this regex…



I think LucasDemea’s content filtering solution is suitable for Gutenberg. I believe there is no Gutenberg replacement for wp_get_attachment_image yet.



We might need to consider adding <noscript> image blocks to the content filter functions. Since we are removing the ‘src’ bit of images it could be bad for Google.


1 Like