Are you a GenerateCustomer?

Do you have an active GP Premium or GenerateBlocks Pro license key?

Create a GenerateSupport account for GeneratePress and GenerateBlocks support in one dedicated place.

Create an account
Already have a GenerateSupport account? Login

Just browsing?

Feel free to browse the forums. Support for our free versions is provided on WordPress.org (GeneratePress, GenerateBlocks).

Want to become a premium user? Learn more below.

Lazy Loading Missing for Images (in Query Loop)

  • I’m using Elements -> Block/Loop for some archive pages and the native GP-archives (Customiser->Layout->Blog->Archive) for others.

    – Native GP-archives are assinging loading=”lazy” for images below the fold as expected in WP.
    – Images in the Block/Loop don’t – neither with “GP Dynamic Images” nor with “GB-Image” & Dynamic Tags in the loop.

    fyi: I don’t use wp_omit_loading_attr_threshold in my functions.php

    Any ideas on how to get back lazy loading for my images?

    Thanks in advance
    Stefanski

  • Hi there,

    I can not replicate the issue.

    Can you link me to the page in question?

  • Link in private Infobox

  • You are using the v1 query loop block. Is there any chance you can use the v2 query block instead?

  • Yes, see PM, please.

  • Hi Stefanski,

    In your V2 Query block, the loading="lazy" attribute is present on the images. You can see it here:
    https://cln.sh/jG7L8nvFRTz7yZPS1Sj0

  • Hi Alvind,

    great, thank you – I missed it, because the page was still cached.

    Stefan

  • Glad to hear that 🙂

  • Hi Alvin,

    one more thing: Now every image inside the loop has loading=”lazy”.
    How can I remove lazy for the first x images in this loop-setup?

    None of the usually suggested methods (omit threshhold etc) seems to work for the Loop-Block.

    Tanks
    Stefan

  • Hi Stefan,

    By default, the first image in the loop won’t have the lazy-loading attribute. Are you looking to remove that attribute from additional images as well, for example the first three images? Is that the scenario you’re trying to achieve?

  • Yes, exactly. I need the option to unset “lazy” for the first x images and ideally add “eager” to them.

    btw: my first image in the loop has the lazy-attribute, too.

  • additional info in PM

  • Hi Stefan,

    Try this method:

    add_action('template_redirect', function() {
        if (!is_archive() && !is_home()) {
            return;
        }
        ob_start(function($html) {
            $eager_count = 3; // Number of images to set as eager
            $counter = 0;
            $html = preg_replace_callback(
                '/<img\b([^>]*)\bloading=["\']lazy["\']([^>]*)>/i',
                function($matches) use (&$counter, $eager_count) {
                    $counter++;
                    if ($counter <= $eager_count) {
                        $tag = '<img' . $matches[1] . $matches[2] . '>';
                        $tag = str_replace('<img', '<img loading="eager"', $tag);
                        if ($counter === 1) {
                            $tag = str_replace('<img', '<img fetchpriority="high"', $tag);
                        }
                        return $tag;
                    }
                    return $matches[0];
                },
                $html
            );
            return $html;
        });
    });

    Adjust $eager_count to control how many images get loading="eager". The first image also gets fetchpriority="high" which helps with LCP. Adjust the is_archive() / is_home() condition to match whichever pages your loop template displays on.

  • Hi George,

    thanks for the code – this works although the method is somewhat “brutal”…

    Is there no more elegant method than manipulating the HTML output using regex?

    Thanks
    Stefan

  • Hello,

    I found a more targeted solution using a GenerateBlocks internal filter. This hooks directly into GB’s rendering pipeline, so it only affects media blocks inside query loops — no output buffer or page type conditions needed:

    add_filter('generateblocks_before_dynamic_tag_replace', function($content, $args) {
        $block_name = $args['block']['blockName'] ?? '';
    
        if ('generateblocks/media' !== $block_name) {
            return $content;
        }
    
        if (!class_exists('WP_HTML_Tag_Processor')) {
            return $content;
        }
    
        $in_query = isset($args['instance']->context['generateblocks/loopItem']);
    
        if (!$in_query) {
            return $content;
        }
    
        $loop_index = $args['instance']->context['generateblocks/loopIndex'] ?? 0;
        $eager_count = 3;
    
        $processor = new WP_HTML_Tag_Processor($content);
    
        if ($processor->next_tag('img')) {
            if ($loop_index <= $eager_count) {
                $processor->set_attribute('loading', 'eager');
    
                if (1 === $loop_index) {
                    $processor->set_attribute('fetchpriority', 'high');
                }
            } else {
                $processor->set_attribute('loading', 'lazy');
            }
    
            $content = $processor->get_updated_html();
        }
    
        return $content;
    }, 10, 2);

    It uses GB’s loop context to identify the position of each image in the loop, then sets loading="eager" and fetchpriority="high" on the first image for LCP, loading="eager" on images 2-3, and explicit loading="lazy" on the rest. You can adjust the $eager_count value if needed.

  • Hi George,

    the new code works like a charm.
    Thank you for your support
    Stefan

Viewing 16 posts - 1 through 16 (of 17 total)
  • You must be logged in to reply to this topic.