Table of contents of the article:
In recent years, metrics related to Website Performance have become central to those who develop or manage websites. The advent of Core Web Vitals, in particular of the parameter LCP (Largest Contentful Paint), has brought about a radical change in the way developers optimize images in their projects.
Yet, in an increasingly complex ecosystem like that of Wordpress, where plugins and page builders heavily intervene on the markup, achieving correct browser behavior is not so obvious.
One of the most frequent cases concerns precisely Elementor, the famous visual page builder, and the way it handles lazy loading, preloading, and image prioritizationIn this article we delve into how image uploading in WordPress really works, because often the “hero” images (the ones visible above the fold, or above the fold) are not treated as they should be, and how to fix them permanently with a simple PHP fix.
The basic problem: how WordPress handles images
Starting with WordPress 5.5, core automatically introduced the attribute loading="lazy" on all images inserted in the contents.
This behavior has a clear goal: reduce initial loading time avoiding downloading resources that are not visible to the user on first rendering.
In practice, if an image is below the fold (below the fold), is loaded only when the visitor scrolls down the page. This is a smart solution to reduce the overall weight of the page, but it has an important side effect:
Even the main image—which often represents the largest and most visible content when the page opens—risks being loaded late, penalizing the LCP metric.
The browser does not distinguish beforehand which image is more important, and lazy-loads it indiscriminately.
WordPress does its best to handle some exceptions automatically, but when page builders like Elementor intervene, the markup changes, and the standard detection mechanisms no longer work as expected.
Elementor lazy load interference
Elementor often applies its own system of lazy-loading on images, overlapping with that of WordPress.
In many installations, the attribute loading="lazy" It is added directly from the image or gallery widgets, or inherited from optimization plugins such as NitroPack, WP Rocket or LiteSpeed Cache.
The result?
The “hero” images — that is, those positioned above the fold —they are still loaded late, because they are marked as “lazy”.
Google PageSpeed Insights or Lighthouse, when analyzing the page, often return messages such as:
Essentially, the main image is not considered “priority” by the browser, and this worsens the visual perception of loading, even if the server and network are very fast.
Understanding above-the-fold and the role of the LCP image
The term above the fold (In Italian above the fold) comes from the world of printed paper: in newspapers, everything in the top half of the front page — the one visible even when the newspaper was folded — was considered the most important content.
In web design and front-end development the concept has remained the same: the above the fold is the part of the page visible to the user without needing to scroll, that is, the one that appears immediately when the site is opened.
This portion has a fundamental role, because it represents the first visual impact with the user.
This is where your initial opinion about the speed, reliability, and quality of the site is formed.
If the area above the fold loads slowly, the user perceives the site as “heavy” or “slow,” even if the rest of the page performs flawlessly.
For this reason, all elements above the fold should load in the shortest possible time: hero images, logos, main texts, calls-to-action and backgrounds of the first block.
It's not just a question of aesthetics, but of user experience and conversionThe quicker a user sees content, the more likely they are to stay on the site and interact.
Google measures this very perception through one of its key metrics Core Web VitalsCalled LCP (Largest Contentful Paint).
This metric evaluates how long does the visually largest element take — which is usually a hero image, a banner, or a large block of text — to appear completely on the screen.
Un LCP good that's what happens within 2,5 seconds from the moment the page starts loading.
Above this threshold, the experience is perceived as slow, and PageSpeed scores and Core Web Vitals they suffer from it.
The problem is that if the main image is loaded with loading="lazy" (i.e. in “lazy” mode) or without adequate network priority, the browser will download it only after it has completed parsing the entire HTML document.
In other words, the image file arrives too late than the browser could already display it, causing a visible delay in the appearance of the main content.
This means that even on fast servers, optimized CDNs and well-configured sites, the user experience may be slow just because of an incorrect HTML attribute or lack of proper loading priority.
This is why today tools like the fetchpriority="high" and preloading tags have become crucial: they serve to explicitly communicate to the browser which Resources deserve absolute priority, that is, those that appear above the fold and that define the first impression of the site.
The importance of preloading
To avoid these delays, you can tell your browser to preload critical resources through the HTML element <link rel="preload">.
A preload forces the browser to download the resource (image, font, video, CSS or JS) just discovered in the document, even if it is not yet needed for rendering.
Classic example for a hero image:
This approach ensures that the image is already available in the cache when the browser encounters it in the markup. <img>.
However, this is not always the most practical solution in WordPress, because:
- adding dynamic preloads requires custom code;
- if a CDN or plugin rewrites URLs, you risk preloading a different file;
- And most importantly, if the page builder changes the DOM order, preloading may lose its effectiveness.
The fetchpriority attribute: the modern shortcut
In 2022, the main browsers (Chrome, Edge, Safari) introduced a new native attribute:
fetchpriority="high"
Unlike preload, which forces the download to begin early, fetchpriority it does not change the time at which the resource is requested, but influences the connection priority while loading.
It is a “lighter” and safer directive to manage:
the browser still decides when to download the image, but if it finds fetchpriority="high" on an image “above the fold”, it will put it at the end before the others.
Example:
The combined use of loading="eager" (download now) and fetchpriority="high" (high network priority) is today the most effective way to ensure fast LCP, even without manual preloading.
Elementor and the Problem of Markup Control
In theory, it would be enough to set the correct attributes on the <img> that appear in hero section or slider above the fold.
But Elementor, like many visual builders, does not expose directly these attributes in the widget fields.
Each image inserted into an Elementor section or column is actually enclosed in multiple layers of <div>, wrapper, overlay, lazy loader, or animations.
Consequently:
- the browser cannot find the image in the initial document (because it is hidden or loaded via JS),
fetchpriority="high"cannot be added manually,- e
loading="lazy"It remains active even where it shouldn't.
This behavior has been reported by many users, but until now Elementor has not introduced a native option to easily exclude an image from lazy loading or assign it a high priority.
The real context: LCP images in Elementor
Let's take a concrete example.
Imagine having a “Hero” block in Elementor, with a large background image or a column image as the main visual element.
The resulting markup might look like this:
<div class="elementor-element elementor-element-123 heroimage">
<img decoding="async" loading="lazy" src="https://example.com/wp-content/uploads/hero.jpg" alt="Hero section">
</div>
Google PageSpeed will tell you that:
- slow loading (
loading="lazy") is not appropriate for the LCP image, - and that you must apply
fetchpriority="high".
But Elementor doesn’t let you change these attributes from the interface, and WordPress still marks it as “lazy” by default.
From here comes the need to intervene downstream, that is, after the page has been generated, to correct the markup.
Above the fold and visual priority
Not all images on the page deserve the same priority.
The only sensible criterion is the visual position: images above the fold should be downloaded immediately, the others should not.
The problem is that Elementor doesn't distinguish between the two cases. All images are treated equally, and even a hero image ends up with loading="lazy".
This means that the user may see the navbar and some text, but not the main image for a second or two, even on fast connections.
Technically, the browser downloads secondary resources (icons, background images, media hidden in tabs) first, and only then the largest image on the page.
The visual effect is a white “flash” or a layout that completes late, penalizing the user experience and Core Web Vitals.
The ideal solution: a fix on the output buffer side
An elegant solution is intercept HTML output generated by Elementor and fix it before it is sent to the browser.
This approach has several advantages:
- requires no changes to widgets or templates;
- does not alter the functioning of Elementor;
- This only applies to the frontend (not the editor).
In practice, we can use PHP output buffering (ob_start()) to capture the HTML generated by the page, look for images that are inside a section with a specific class (e.g. .heroimage), and automatically apply the correct attributes.
Our fix for Elementor
To permanently solve the lazy load and fetchpriority problem on main images, we share below a open source fix which can be inserted into the file functions.php of the theme or, better yet, in a small mu-plugins customized.
add_action('template_redirect', function () {
if (is_admin() || wp_doing_ajax()) return;
// Escludi le richieste AJAX di Elementor
if (isset($_REQUEST['action']) && strpos($_REQUEST['action'], 'elementor') !== false) {
return;
}
ob_start('elementor_add_fetchpriority_to_target_images');
});
function elementor_add_fetchpriority_to_target_images($html) {
// Trova tutti i div con classe heroimage e le loro img
$pattern = '/<div[^>]*class="[^"]*heroimage[^"]*"[^>]*>.*?<img([^>]*)>/is';
$html = preg_replace_callback($pattern, function ($matches) {
$img_tag = $matches[0];
// Aggiungi fetchpriority="high" se non presente
if (strpos($img_tag, 'fetchpriority=') === false) {
$img_tag = preg_replace('/<img/', '<img fetchpriority="high"', $img_tag);
}
// Rimuovi loading="lazy"
$img_tag = preg_replace('/\s*loading=(["\'])lazy\1/', '', $img_tag);
return $img_tag;
}, $html);
return $html;
}
How it works
- Hook
template_redirect
The code only takes effect on the frontend of your site, after WordPress has loaded the template but before sending the HTML to the browser. - Output buffering (
ob_start)
Capture the full page output generated by Elementor. - Regex on “heroimage” classes
Search all<div>which contain the classheroimageand identify the relevant<img>inside them. - Markup fix
- Removes any
loading="lazy". - Adds
fetchpriority="high"if not present.
This way, the main image is treated as a priority, even if Elementor or WordPress continues to force lazy loading.
- Removes any
- Returning correct output
The modified HTML is returned to the browser, ensuring that the main images are downloaded immediately.
Why it's a smart approach
This fix will not interfere with the normal functioning of Elementor, nor with optimization plugins such as NitroPack, WP Rocket, or LiteSpeed Cache.
It works only in the output phase and only for elements that explicitly have a class .heroimage.
This way you can decide, directly from the builder, which sections should contain priority images.
It is enough to add the class heroimage to the hero's main section or container, and the script will do everything by itself.
Integration with caching plugins
Since the fix affects the final HTML output, it works perfectly even in the presence of server-side caching systems or optimization plugins.
Once the buffer is modified the first time, the optimized version is cached, so the computational cost is zero on subsequent requests.
It is recommended that you clear your cache after activation to ensure that pages are regenerated with the new attributes.
Result on PageSpeed Insights
After applying this fix, the difference in reports of Google Insights PageSpeed o Lighthouse it's immediate:
- The “Must apply fetchpriority=high” entry disappears.
- The hero image is marked as “detectable in the source document.”
- The LCP metric improves markedly, often with a gain of 80-100ms on the perceived total rendering time.
Possible extensions
Those who want more granular control can extend the script in several ways:
- Apply the fix only to certain templates or post types.
- Also add
loading="eager"odecoding="async"in addition tofetchpriority="high". - Manage multiple CSS classes (e.g.
.hero,.banner,.featured).
Here is an example of a variant that includes loading="eager":
<?php
if (strpos($img_tag, 'loading=') === false) {
$img_tag = preg_replace('/<img/', '<img loading="eager"', $img_tag);
}
?>
Conclusions
Efficient image loading is one of the most important aspects to ensure high performance and scores Core Web Vitals ottimali.
In WordPress, and in particular with Elementor, the default behavior can, however, hinder the achievement of these goals, due to the indiscriminately applied lazy loading.
By intervening directly on the HTML output and correcting the main images with fetchpriority="high", you can restore the ideal browser behavior:
download the most important resources first, making the user experience smooth and visually immediate.
The proposed fix is simple, effective, and fully compatible with any hosting environment.
Just add a CSS class (heroimage) to the sections above the fold and let the script take care of the rest.
A small technical intervention, but with a large impact on perceived load times and LCP score.