• Resolved xninedesign

    (@x9design)


    Hi Kadence community,

    Kadence Blocks does a great job handling most image types, but I’ve noticed that when SVG files are inserted (e.g., via the Advanced Image block or other modules), the generated <img> tags do not include width and height attributes. This can lead to layout shifts (CLS issues) because browsers can’t reserve space for the image until the SVG is loaded and its intrinsic dimensions are known.

    To solve this, I’ve created a small PHP function that hooks into the_content filter. It detects <img> tags with .svg sources, reads the SVG file’s viewBox attribute to determine the aspect ratio, calculates appropriate dimensions that fit within a 300×300 px box while preserving the ratio, and adds fixed width and height attributes. If the SVG has no viewBox, the tag is left unchanged.

    The function is optimized for performance:

    • Works only on frontend
    • Reads local SVG files directly from disk (no HTTP overhead)
    • Caches calculated dimensions using transients (perfect if you have object caching like Redis)
    • Strictly limited to local files and includes size safeguards

    Here’s the complete code you can add to your child theme’s functions.php (or a site-specific plugin):

    function kadence_svg_add_fixed_dimensions( $content ) {
    if ( is_admin() ) {
    return $content;
    }

    preg_match_all( '/<img([^>]*)src=[\'"]([^\'"]*\.svg)[\'"]([^>]*)>/i', $content, $matches, PREG_SET_ORDER );

    if ( empty( $matches ) ) {
    return $content;
    }

    foreach ( $matches as $match ) {
    $full_tag = $match[0];
    $before_src = $match[1];
    $src = $match[2];
    $after_src = $match[3];

    // Build full URL
    $svg_url = $src;
    if ( ! preg_match( '/^https?:\/\//i', $src ) ) {
    $svg_url = site_url( $src );
    }

    // Only local files
    if ( strpos( $svg_url, home_url() ) !== 0 ) {
    continue;
    }

    // Convert URL to file path
    $upload_dir = wp_get_upload_dir();
    $base_url = trailingslashit( $upload_dir['baseurl'] );
    $base_dir = trailingslashit( $upload_dir['basedir'] );

    if ( strpos( $svg_url, $base_url ) === 0 ) {
    $file_path = $base_dir . substr( $svg_url, strlen( $base_url ) );
    } else {
    $file_path = ABSPATH . ltrim( parse_url( $svg_url, PHP_URL_PATH ), '/' );
    }

    // Cache key based on file path
    $cache_key = 'kadence_svg_dims_' . md5( $file_path );
    $dimensions = get_transient( $cache_key );

    if ( false === $dimensions && file_exists( $file_path ) ) {
    $svg_content = file_get_contents( $file_path );

    // Safety: limit file size
    if ( strlen( $svg_content ) > 100 * 1024 ) {
    continue;
    }

    if ( preg_match( '/viewBox=[\'"]([^\'"]*)[\'"]/', $svg_content, $vb_match ) ) {
    $viewbox = trim( $vb_match[1] );
    $parts = preg_split( '/\s+/', $viewbox );
    if ( count( $parts ) === 4 ) {
    $vb_width = floatval( $parts[2] );
    $vb_height = floatval( $parts[3] );
    if ( $vb_width > 0 && $vb_height > 0 ) {
    $aspect_ratio = $vb_width / $vb_height;
    $max = 300;

    if ( $aspect_ratio > 1 ) {
    $new_width = $max;
    $new_height = round( $max / $aspect_ratio );
    } elseif ( $aspect_ratio < 1 ) {
    $new_height = $max;
    $new_width = round( $max * $aspect_ratio );
    } else {
    $new_width = $new_height = $max;
    }

    $dimensions = array( $new_width, $new_height );
    set_transient( $cache_key, $dimensions, MONTH_IN_SECONDS );
    }
    }
    }
    }

    if ( ! $dimensions ) {
    continue;
    }

    list( $new_width, $new_height ) = $dimensions;

    // Remove any existing width/height attributes
    $attributes = $before_src . $after_src;
    $attributes = preg_replace( '/\s+width=[\'"][^\'"]*[\'"]/', '', $attributes );
    $attributes = preg_replace( '/\s+height=[\'"][^\'"]*[\'"]/', '', $attributes );

    // Build new tag
    $new_tag = '<img' . $attributes . ' src="' . esc_attr( $src ) . '" width="' . $new_width . '" height="' . $new_height . '">';

    $content = str_replace( $full_tag, $new_tag, $content );
    }

    return $content;
    }
    add_filter( 'the_content', 'kadence_svg_add_fixed_dimensions', 99 );

    Feel free to adjust the $max = 300; value if you want a different maximum size.

    This has worked perfectly on my private site with Kadence Blocks and eliminates layout shifts for SVGs. Hope it helps someone else too!

    If the Kadence team sees this – it would be awesome to have native width/height support for SVGs in a future update. 😊

    Thanks!

Viewing 5 replies - 1 through 5 (of 5 total)
  • Plugin Support aapc

    (@aapc)

    Hi @x9design,

    Thank you for taking the time to publish your findings.

    If you are using SVGs quite often, I would suggest checking out the Kadence Vector Graphic block instead of the Image or Image Adv block. Although this block does not add width and height attributes to the SVG, it does tend to work better with SVGs than an image tag.

    One way to control the proportions of your SVGs would be to add dimensions to the SVG itself. You can see how to do this here: https://www.w3schools.com/html/html5_svg.asp. You can still scale the SVG using the Min-width setting in the Vector block and the height will auto-scale. In my tests, I saw no CLS at all without the need for additional code snippets.

    If you’re worried about adding alt text, the SVG specification also includes a <title> tag that you can add to your SVG: https://developer.mozilla.org/en-US/docs/Web/SVG/Reference/Element/title

    And lastly, the Vector block will add your SVG inline, reducing the need for another browser call to fetch the SVG file when using the image tag.

    Give that a try…I hope this helps!

    All the best,
    Anthony

    Thread Starter xninedesign

    (@x9design)

    Hello @aapc
    Thank you for your reply.
    I created this post in response to a specific situation I encountered while working with Kadence Blocks.

    When adding an SVG file as an image using the “Image (Adv)” block, there are significantly more options for controlling how the graphic is rendered. The most important one is the ability to wrap the generated <img> element with an anchor tag <a>.

    In contrast, the “Vector Graphic” block only allows embedding inline SVG markup directly into the page content. The available settings are limited to defining a maximum width (as you mentioned) and adjusting spacing (padding/margins) for the entire SVG container.

    As a result, there are real-world use cases where inserting an SVG as an image element is more practical. This, however, leads to an <img> element being rendered without width and height attributes. This exact issue was the primary reason for implementing my function — Google PageSpeed Insights reports consistently flagged the missing attributes as a performance problem. When an SVG is used as a clickable element, my solution addresses this limitation.

    If the theme were to provide a way to wrap inline SVG graphics with an <a> element, the Image (Adv) block would no longer be necessary in such cases, because when SVG markup is placed directly inside an anchor tag, PageSpeed Insights does not expect width and height attributes, as this requirement applies exclusively to <img> elements.

    Kind regards
    Christopher

    • This reply was modified 2 months, 1 week ago by xninedesign.
    Plugin Support aapc

    (@aapc)

    Hi @x9design,

    Given the fact that SVG graphics are scalable, it really doesn’t make sense to use an img tag with fixed dimensions unless the SVG file is more efficient (i.e. smaller file-size) than a bitmap version. Using a percent-based size plays to the strength of the vector format making it the ultimate responsive graphic. Placing it inside a container such as a Section block to control the size will eliminate any CLS.

    It would be nice if the Vector Graphics block included a link option. We actually have a feature request for this. You can view the request here. Feel free to add your vote! As an alternative, you can use the Section block Overlay Link to add a link and title.

    In the end, whatever works best for your situation is likely the best choice. I do think adding a link and title would be a good addition to the block.

    Thanks for your feedback!
    Anthony

    Plugin Support architalevelup

    (@architabasandrai20)

    Hi @x9design

    Just checking in on this topic. Do you still need assistance here?

    Please note that you can also submit a support ticket via these channels.

    FREE Users: https://www.kadencewp.com/help-center/free-support

    Premium Users: https://www.kadencewp.com/help-center/premium-support

    Thank you for your understanding!

    Regards,
    Archita

    Plugin Support architalevelup

    (@architabasandrai20)

    Hi @x9design

    This topic has been inactive from while. I’ll proceed to mark this as resolved.

    Please note that you can also submit a support ticket via these channels.

    Thank you for your understanding!

Viewing 5 replies - 1 through 5 (of 5 total)

You must be logged in to reply to this topic.