Workaround: Fallback image for missing featured image in query loop (GB 2.x)

  • st.unterpertinger

    When using a generateblocks/media block with {{featured_image}} in a query loop, posts without a featured image render nothing — no image, no placeholder, just empty space in the layout.

    Filters like generateblocks_dynamic_image_fallback do not appear to fire in GB 2.x with the new dynamic tags system, so the usual PHP approach does not work.

    ## Workaround

    The only solution that worked is the native WordPress render_block filter. It intercepts the block output before it reaches the page — if the output is empty (no featured image), it returns a randomly selected fallback image instead.

    Fallback images are uploaded to the media library with filenames containing the word dummy (e.g. dummy1.jpg, dummy2.jpg etc.). The code searches for them automatically by filename, so no IDs need to be hardcoded. If a fallback image is deleted from the library, the code simply picks one of the remaining ones.

    The fallback only applies on archive and listing pages — single posts and pages are excluded via is_singular().

    
    add_filter( 'render_block', function( $block_content, $block ) {
        if ( $block['blockName'] !== 'generateblocks/media' ) return $block_content;
        if ( trim( $block_content ) !== '' ) return $block_content;
        if ( is_singular() ) return $block_content;
    
        global $wpdb;
        $ids = wp_cache_get( 'dummy_image_ids' );
    
        if ( false === $ids ) {
            $results = $wpdb->get_results(
                "SELECT p.ID FROM {$wpdb->posts} p
                 INNER JOIN {$wpdb->postmeta} pm ON p.ID = pm.post_id
                 WHERE p.post_type = 'attachment'
                 AND pm.meta_key = '_wp_attached_file'
                 AND pm.meta_value LIKE '%dummy%'
                 LIMIT 10"
            );
            $ids = array_map( fn($r) => (int) $r->ID, $results );
            wp_cache_set( 'dummy_image_ids', $ids );
        }
    
        if ( empty( $ids ) ) return $block_content;
    
        $random_id = $ids[ array_rand( $ids ) ];
        $url = wp_get_attachment_image_url( $random_id, 'large' );
    
        return '<img src="' . esc_url( $url ) . '" />';
    }, 10, 2 );
    
  • Hello,

    Nice workaround — render_block interception is a valid approach. If you want something that stays inside the dynamic tags system, you can register your own image tag with the fallback logic built into the callback. The advantage is that GenerateBlocks handles the rendering normally, so the Media block keeps its proper image markup (srcset, image size, link support) instead of a hand-built <img> — and as you’ll notice, the new tag shows up right in the Dynamic Tags selector alongside the built-in ones, so it’s selectable from the UI like any other tag.

    Here’s the registration:

    add_action( 'init', function() {
        new GenerateBlocks_Register_Dynamic_Tag( [
            'title'    => __( 'Featured Image with Fallback', 'textdomain' ),
            'tag'      => 'featured_image_fallback',
            'type'     => 'post',
            'supports' => [ 'source', 'image-size' ],
            'return'   => function( $options, $block, $instance ) {
                $id   = GenerateBlocks_Dynamic_Tags::get_id( $options, 'post', $instance );
                $size = $options['size'] ?? 'large';
                $url  = get_the_post_thumbnail_url( $id, $size );
    
                if ( ! $url ) {
                    global $wpdb;
                    $ids = $wpdb->get_col(
                        "SELECT post_id FROM {$wpdb->postmeta}
                         WHERE meta_key = '_wp_attached_file'
                         AND meta_value LIKE '%dummy%' LIMIT 10"
                    );
                    if ( $ids ) {
                        $random_id = (int) $ids[ array_rand( $ids ) ];
                        $url = wp_get_attachment_image_url( $random_id, $size );
                    }
                }
    
                return GenerateBlocks_Dynamic_Tag_Callbacks::output( $url, $options, $instance );
            },
        ] );
    } );

    Then in your Media block, insert a new tag or modify the existing one and select the Featured Image with Fallback dynamic tag, and leave “Required to render” checked. That way you get the featured image when it exists, a random dummy when it doesn’t, and — if there are no dummy images at all — the block simply doesn’t render, rather than leaving a broken image container.

    This keeps the same fallback behavior you’re already using — any image whose filename contains “dummy”, picked at random — just moved into a proper dynamic tag rather than filtering block output after the fact.

  • got same problem with post query loop in the pages, generateblocks_dynamic_image_fallback works for previous GB, but not working in GB 2.

    Tried the Workaround code, didn’t work. (removed the is_singular() result still the same).

    Topics covering fallback images:
    query-loop-default-image-or-no-image-block-if-there-is-no-featured-image
    placeholder-images-in-grid

    Tried all the code there, also didn’t work.

    I’d rather following this topic, than creating new one. For now..

  • Can confirm, George’s code works for me.

    Thank you both.

  • Good to know!

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