[{"data":1,"prerenderedAt":1110},["ShallowReactive",2],{"content:\u002Fimage-media-optimization\u002Flazy-loading-images-without-hurting-lcp\u002Ffixing-lazy-loaded-images-that-delay-lcp\u002F":3,"surroundings:\u002Fimage-media-optimization\u002Flazy-loading-images-without-hurting-lcp\u002Ffixing-lazy-loaded-images-that-delay-lcp\u002F":1102},{"id":4,"title":5,"body":6,"description":1082,"extension":1083,"meta":1084,"navigation":1096,"path":1097,"seo":1098,"stem":1100,"__hash__":1101},"content\u002Fimage-media-optimization\u002Flazy-loading-images-without-hurting-lcp\u002Ffixing-lazy-loaded-images-that-delay-lcp\u002Findex.md","Fixing Lazy-Loaded Images That Delay LCP",{"type":7,"value":8,"toc":1064},"minimark",[9,13,33,159,164,167,224,230,234,239,251,255,270,274,288,292,302,306,310,319,450,453,457,468,569,572,576,579,683,686,690,699,768,771,775,778,859,862,866,877,894,897,998,1011,1015,1050,1054,1057,1060],[10,11,5],"h1",{"id":12},"fixing-lazy-loaded-images-that-delay-lcp",[14,15,16,17,22,23,27,28,32],"p",{},"You enabled lazy loading, initial bytes dropped, and then field data showed LCP regressed from roughly 2.1s to 3.4s on mobile — because the deferral rule caught the hero image. This is the most common self-inflicted LCP regression, and it is the specific recovery path for the broader ",[18,19,21],"a",{"href":20},"\u002Fimage-media-optimization\u002Flazy-loading-images-without-hurting-lcp\u002F","lazy loading without hurting LCP"," workflow within the ",[18,24,26],{"href":25},"\u002Fimage-media-optimization\u002F","Image & Media Optimization"," discipline. The symptom is unmistakable: a deferral change that was supposed to be a pure win quietly pushed the ",[18,29,31],{"href":30},"\u002Fcore-web-vitals-measurement\u002Fmeasuring-lcp-with-chrome-devtools\u002F","Largest Contentful Paint"," element off the early-discovery path, and now the loading metric is over its 2.5s budget. This page diagnoses why and fixes it in order of impact.",[14,34,35],{},[36,37,44,45,44,49,44,53,44,63,44,70,44,78,44,83,44,89,44,94,44,102,44,107,44,113,44,117,44,121,44,125,44,129,44,132,44,134,44,139,44,143,44,146,44,149,44,154,44],"svg",{"xmlns":38,"viewBox":39,"width":40,"role":41,"ariaLabel":42,"style":43},"http:\u002F\u002Fwww.w3.org\u002F2000\u002Fsvg","0 0 760 300","100%","img","Before and after waterfall showing a lazy hero discovered late versus an eager hero in the first request wave","height:auto;max-width:760px;display:block;margin:1.75rem auto;font-family:inherit;color:#001d3d"," ",[46,47,48],"title",{},"Lazy vs eager hero discovery",[50,51,52],"desc",{},"The lazy hero starts after layout, pushing LCP past 2.5s; the eager hero starts in the first wave and lands under budget.",[54,55],"rect",{"x":56,"y":56,"width":57,"height":58,"rx":59,"fill":60,"stroke":61,"style":62},"1","758","298","10","none","currentColor","stroke-opacity:0.18",[64,65,69],"text",{"x":66,"y":67,"fill":61,"style":68},"24","36","font-size:17px;font-weight:700","Hero request timing vs LCP budget",[71,72],"line",{"x1":73,"y1":74,"x2":73,"y2":75,"stroke":76,"style":77},"200","252","60","#b8860b","stroke-opacity:0.8;stroke-dasharray:4 4",[64,79,82],{"x":73,"y":80,"fill":61,"style":81},"274","font-size:12px;text-anchor:middle","LCP budget 2.5s",[64,84,88],{"x":85,"y":86,"fill":61,"style":87},"40","92","font-size:13px;font-weight:600","Before",[64,90,93],{"x":85,"y":91,"fill":61,"style":92},"110","font-size:12px","lazy",[54,95],{"x":96,"y":97,"width":98,"height":99,"rx":100,"fill":61,"stroke":61,"style":101},"120","78","80","20","3","stroke-opacity:0.4;fill-opacity:0.12",[64,103,106],{"x":104,"y":86,"fill":61,"style":105},"160","font-size:11px;text-anchor:middle","HTML\u002FCSS",[54,108],{"x":109,"y":97,"width":110,"height":99,"rx":100,"fill":111,"stroke":111,"style":112},"300","220","#0466c8","fill-opacity:0.16",[64,114,116],{"x":115,"y":86,"fill":61,"style":105},"410","hero fetch, late",[64,118,120],{"x":119,"y":86,"fill":76,"style":92},"540","paints 3.4s",[64,122,124],{"x":85,"y":123,"fill":61,"style":87},"172","After",[64,126,128],{"x":85,"y":127,"fill":61,"style":92},"190","eager",[54,130],{"x":96,"y":131,"width":98,"height":99,"rx":100,"fill":61,"stroke":61,"style":101},"158",[64,133,106],{"x":104,"y":123,"fill":61,"style":105},[54,135],{"x":96,"y":136,"width":75,"height":99,"rx":100,"fill":137,"stroke":76,"style":138},"186","#ffc300","fill-opacity:0.3",[64,140,142],{"x":141,"y":73,"fill":61,"style":105},"150","hero",[64,144,145],{"x":127,"y":73,"fill":61,"style":92},"first wave + fetchpriority=high",[64,147,148],{"x":119,"y":123,"fill":111,"style":92},"paints 2.1s",[71,150],{"x1":66,"y1":151,"x2":152,"y2":151,"stroke":61,"style":153},"228","736","stroke-opacity:0.3",[64,155,158],{"x":66,"y":156,"fill":61,"style":157},"248","font-size:12px;fill-opacity:0.8","Scope eager to the one LCP element; everything below the fold stays lazy.",[160,161,163],"h2",{"id":162},"rapid-diagnosis-a-five-point-devtools-checklist","Rapid Diagnosis: A Five-Point DevTools Checklist",[14,165,166],{},"Before changing code, confirm the hero is actually the problem and that deferral is actually the cause. Run this checklist in Chrome DevTools against a throttled mobile profile (Fast 3G, 4x CPU):",[168,169,170,178,193,199,218],"ol",{},[171,172,173,177],"li",{},[174,175,176],"strong",{},"Identify the LCP element."," Run a Performance trace and read the LCP marker in the Timings track — DevTools names the exact element. Confirm it is the hero image and not a heading or background.",[171,179,180,188,189,192],{},[174,181,182,183,187],{},"Check the ",[184,185,186],"code",{},"loading"," attribute."," Select that element in the Elements panel. If it shows ",[184,190,191],{},"loading=\"lazy\"",", you have found the regression in one step.",[171,194,195,198],{},[174,196,197],{},"Check request start time."," In the Network panel, filter to images and sort by start time. A correctly eager hero starts in the first wave; a deferred one starts hundreds of milliseconds late, after layout settles.",[171,200,201,204,205,209,210,213,214,217],{},[174,202,203],{},"Check the initiator."," Hover the hero request's Initiator column. An eager hero is initiated by the ",[206,207,208],"em",{},"preload scanner \u002F parser","; a lazy one is initiated by ",[206,211,212],{},"layout"," or ",[206,215,216],{},"script"," much later.",[171,219,220,223],{},[174,221,222],{},"Decompose LCP phases."," In the LCP entry, compare load delay and load time. A large load delay with a small render delay points squarely at late discovery — the deferral fingerprint.",[14,225,226,227,229],{},"If steps 2 through 4 all point at deferral, the rest of this page is your fix. If the ",[184,228,186],{}," attribute is already eager but the request still starts late, the problem is discovery priority rather than deferral, and the priority fixes below still apply.",[160,231,233],{"id":232},"root-cause-analysis-four-ways-a-hero-gets-deferred","Root Cause Analysis: Four Ways a Hero Gets Deferred",[235,236,238],"h3",{"id":237},"root-cause-1-a-blanket-lazy-loading-default","Root cause 1: A blanket lazy-loading default",[14,240,241,242,244,245,247,248,250],{},"The most frequent cause is a global rule — a template default, a build transform, or an image component that defaults ",[184,243,186],{}," to ",[184,246,93],{}," for every image. The hero is rendered through the same path as every other image, so it inherits the default and gets skipped by the preload scanner. The mechanism is exactly the intended behavior of ",[184,249,191],{},": the browser deliberately omits lazy images from the early parse-time fetch, so the hero waits until layout proves it is near the viewport — which, for an above-the-fold element, still means it loses the race against CSS, fonts, and the document itself.",[235,252,254],{"id":253},"root-cause-2-a-framework-image-component-defaulting-to-lazy","Root cause 2: A framework image component defaulting to lazy",[14,256,257,258,213,261,263,264,266,267,269],{},"Some framework image components default to lazy loading and require an explicit ",[184,259,260],{},"priority",[184,262,128],{}," prop to opt out. A hero authored with the default component looks correct in source but renders ",[184,265,191],{}," in the DOM. This is insidious because the component abstracts the attribute away, so a code review of the JSX or template shows no ",[184,268,186],{}," attribute at all — you only see the regression by inspecting the rendered HTML.",[235,271,273],{"id":272},"root-cause-3-intersectionobserver-hiding-the-url-behind-data-src","Root cause 3: IntersectionObserver hiding the URL behind data-src",[14,275,276,277,280,281,284,285,287],{},"A custom lazy-loading implementation that stores the real URL in ",[184,278,279],{},"data-src"," and swaps it into ",[184,282,283],{},"src"," on intersection makes the hero invisible to the preload scanner entirely — there is no ",[184,286,283],{}," to discover. Worse, the hero now waits for the observer's JavaScript to parse, execute, and fire before its fetch even begins, stacking script-execution latency on top of the deferral. For the LCP element this is the slowest possible path.",[235,289,291],{"id":290},"root-cause-4-eager-attribute-present-but-discovery-still-late","Root cause 4: Eager attribute present but discovery still late",[14,293,294,295,298,299,301],{},"Sometimes the hero is genuinely ",[184,296,297],{},"loading=\"eager\""," yet still starts late because it is injected by client-side JavaScript, sits behind a render-blocking stylesheet, or competes with other resources for priority. The attribute is correct, but the element is not discoverable in the initial HTML or not prioritized once discovered. This is a discovery problem rather than a deferral problem, and it is fixed with priority hints and preloading rather than by changing ",[184,300,186],{},".",[160,303,305],{"id":304},"step-by-step-resolution-ordered-by-impact","Step-by-Step Resolution, Ordered by Impact",[235,307,309],{"id":308},"fix-1-make-the-hero-eager","Fix 1: Make the hero eager",[14,311,312,313,315,316,318],{},"Remove ",[184,314,191],{}," from the LCP image so the preload scanner discovers and fetches it during the initial HTML parse. Set it explicitly to ",[184,317,128],{}," to make the intent obvious and resistant to a future blanket default. This is the single highest-impact change and reverses the core of the regression.",[320,321,326],"pre",{"className":322,"code":323,"language":324,"meta":325,"style":325},"language-html shiki shiki-themes github-light-high-contrast github-light-high-contrast github-light-high-contrast","\u003Cimg\n  src=\"hero-1200.jpg\"\n  srcset=\"hero-800.jpg 800w, hero-1200.jpg 1200w, hero-1600.jpg 1600w\"\n  sizes=\"(max-width: 768px) 100vw, 1100px\"\n  width=\"1600\" height=\"900\"\n  alt=\"Annual conference main stage\"\n  loading=\"eager\"\n  decoding=\"async\">\n\u003C!-- trade-off: eager is correct for the ONE hero only. Do not flip your global\n     default back to eager to fix one image, or you re-introduce the offscreen\n     over-fetch that lazy loading was solving. Scope eager to the LCP element. -->\n","html","",[184,327,328,340,354,365,376,395,406,417,431,438,444],{"__ignoreMap":325},[329,330,332,336],"span",{"class":71,"line":331},1,[329,333,335],{"class":334},"syybb","\u003C",[329,337,339],{"class":338},"s-fAs","img\n",[329,341,343,347,350],{"class":71,"line":342},2,[329,344,346],{"class":345},"sf6mN","  src",[329,348,349],{"class":334},"=",[329,351,353],{"class":352},"s-_DF","\"hero-1200.jpg\"\n",[329,355,357,360,362],{"class":71,"line":356},3,[329,358,359],{"class":345},"  srcset",[329,361,349],{"class":334},[329,363,364],{"class":352},"\"hero-800.jpg 800w, hero-1200.jpg 1200w, hero-1600.jpg 1600w\"\n",[329,366,368,371,373],{"class":71,"line":367},4,[329,369,370],{"class":345},"  sizes",[329,372,349],{"class":334},[329,374,375],{"class":352},"\"(max-width: 768px) 100vw, 1100px\"\n",[329,377,379,382,384,387,390,392],{"class":71,"line":378},5,[329,380,381],{"class":345},"  width",[329,383,349],{"class":334},[329,385,386],{"class":352},"\"1600\"",[329,388,389],{"class":345}," height",[329,391,349],{"class":334},[329,393,394],{"class":352},"\"900\"\n",[329,396,398,401,403],{"class":71,"line":397},6,[329,399,400],{"class":345},"  alt",[329,402,349],{"class":334},[329,404,405],{"class":352},"\"Annual conference main stage\"\n",[329,407,409,412,414],{"class":71,"line":408},7,[329,410,411],{"class":345},"  loading",[329,413,349],{"class":334},[329,415,416],{"class":352},"\"eager\"\n",[329,418,420,423,425,428],{"class":71,"line":419},8,[329,421,422],{"class":345},"  decoding",[329,424,349],{"class":334},[329,426,427],{"class":352},"\"async\"",[329,429,430],{"class":334},">\n",[329,432,434],{"class":71,"line":433},9,[329,435,437],{"class":436},"sIIH1","\u003C!-- trade-off: eager is correct for the ONE hero only. Do not flip your global\n",[329,439,441],{"class":71,"line":440},10,[329,442,443],{"class":436},"     default back to eager to fix one image, or you re-introduce the offscreen\n",[329,445,447],{"class":71,"line":446},11,[329,448,449],{"class":436},"     over-fetch that lazy loading was solving. Scope eager to the LCP element. -->\n",[14,451,452],{},"Expected outcome: the hero moves back into the first request wave; load delay drops by roughly 500–1500ms, recovering most of the regression on its own.",[235,454,456],{"id":455},"fix-2-add-fetchpriorityhigh-to-the-hero","Fix 2: Add fetchpriority=\"high\" to the hero",[14,458,459,460,463,464,301],{},"Discovery alone gets the request issued early; ",[184,461,462],{},"fetchpriority=\"high\""," ensures it is not queued behind less important resources once issued. This is the partner of fix 1 and the detailed mechanics are covered in ",[18,465,467],{"href":466},"\u002Fimage-media-optimization\u002Fimage-cdns-and-fetchpriority\u002Fusing-fetchpriority-to-prioritize-the-lcp-image\u002F","using fetchpriority to prioritize the LCP image",[320,469,471],{"className":322,"code":470,"language":324,"meta":325,"style":325},"\u003Cimg src=\"hero-1200.jpg\" srcset=\"hero-800.jpg 800w, hero-1600.jpg 1600w\"\n     sizes=\"(max-width: 768px) 100vw, 1100px\"\n     width=\"1600\" height=\"900\" alt=\"Annual conference main stage\"\n     loading=\"eager\" fetchpriority=\"high\" decoding=\"async\">\n\u003C!-- trade-off: assign fetchpriority=high to exactly one element. Marking several\n     images high dilutes the signal and the browser reverts to its heuristics,\n     leaving the true LCP image competing for bandwidth again. -->\n",[184,472,473,495,504,527,554,559,564],{"__ignoreMap":325},[329,474,475,477,479,482,484,487,490,492],{"class":71,"line":331},[329,476,335],{"class":334},[329,478,41],{"class":338},[329,480,481],{"class":345}," src",[329,483,349],{"class":334},[329,485,486],{"class":352},"\"hero-1200.jpg\"",[329,488,489],{"class":345}," srcset",[329,491,349],{"class":334},[329,493,494],{"class":352},"\"hero-800.jpg 800w, hero-1600.jpg 1600w\"\n",[329,496,497,500,502],{"class":71,"line":342},[329,498,499],{"class":345},"     sizes",[329,501,349],{"class":334},[329,503,375],{"class":352},[329,505,506,509,511,513,515,517,520,523,525],{"class":71,"line":356},[329,507,508],{"class":345},"     width",[329,510,349],{"class":334},[329,512,386],{"class":352},[329,514,389],{"class":345},[329,516,349],{"class":334},[329,518,519],{"class":352},"\"900\"",[329,521,522],{"class":345}," alt",[329,524,349],{"class":334},[329,526,405],{"class":352},[329,528,529,532,534,537,540,542,545,548,550,552],{"class":71,"line":367},[329,530,531],{"class":345},"     loading",[329,533,349],{"class":334},[329,535,536],{"class":352},"\"eager\"",[329,538,539],{"class":345}," fetchpriority",[329,541,349],{"class":334},[329,543,544],{"class":352},"\"high\"",[329,546,547],{"class":345}," decoding",[329,549,349],{"class":334},[329,551,427],{"class":352},[329,553,430],{"class":334},[329,555,556],{"class":71,"line":378},[329,557,558],{"class":436},"\u003C!-- trade-off: assign fetchpriority=high to exactly one element. Marking several\n",[329,560,561],{"class":71,"line":397},[329,562,563],{"class":436},"     images high dilutes the signal and the browser reverts to its heuristics,\n",[329,565,566],{"class":71,"line":408},[329,567,568],{"class":436},"     leaving the true LCP image competing for bandwidth again. -->\n",[14,570,571],{},"Expected outcome: shaves a further ~100–300ms off load time on contended connections by jumping the request queue.",[235,573,575],{"id":574},"fix-3-fix-the-component-default-not-just-the-instance","Fix 3: Fix the component default, not just the instance",[14,577,578],{},"If the cause was a framework component defaulting to lazy (root cause 2), pass the explicit priority prop on the hero and audit every other above-the-fold use of the component. Invert the policy in your shared wrapper so lazy is opt-out for visible media.",[320,580,584],{"className":581,"code":582,"language":583,"meta":325,"style":325},"language-jsx shiki shiki-themes github-light-high-contrast github-light-high-contrast github-light-high-contrast","\u002F\u002F Next.js example: the hero MUST set priority to escape the lazy default\n\u003CImage\n  src=\"\u002Fhero-1600.jpg\"\n  width={1600}\n  height={900}\n  sizes=\"(max-width: 768px) 100vw, 1100px\"\n  alt=\"Annual conference main stage\"\n  priority   \u002F\u002F sets fetchpriority=high and disables lazy loading for this image\n\u002F>\n{\u002F* trade-off: priority should be set on at most one or two above-the-fold images.\n    Setting it everywhere defeats the purpose and floods the network with eager\n    requests, re-creating the bandwidth contention you are trying to remove. *\u002F}\n","jsx",[184,585,586,591,598,608,621,633,641,649,657,662,670,675],{"__ignoreMap":325},[329,587,588],{"class":71,"line":331},[329,589,590],{"class":436},"\u002F\u002F Next.js example: the hero MUST set priority to escape the lazy default\n",[329,592,593,595],{"class":71,"line":342},[329,594,335],{"class":334},[329,596,597],{"class":338},"Image\n",[329,599,600,602,605],{"class":71,"line":356},[329,601,346],{"class":345},[329,603,349],{"class":604},"sP5qI",[329,606,607],{"class":352},"\"\u002Fhero-1600.jpg\"\n",[329,609,610,612,615,618],{"class":71,"line":367},[329,611,381],{"class":345},[329,613,614],{"class":604},"={",[329,616,617],{"class":345},"1600",[329,619,620],{"class":604},"}\n",[329,622,623,626,628,631],{"class":71,"line":378},[329,624,625],{"class":345},"  height",[329,627,614],{"class":604},[329,629,630],{"class":345},"900",[329,632,620],{"class":604},[329,634,635,637,639],{"class":71,"line":397},[329,636,370],{"class":345},[329,638,349],{"class":604},[329,640,375],{"class":352},[329,642,643,645,647],{"class":71,"line":408},[329,644,400],{"class":345},[329,646,349],{"class":604},[329,648,405],{"class":352},[329,650,651,654],{"class":71,"line":419},[329,652,653],{"class":345},"  priority",[329,655,656],{"class":436},"   \u002F\u002F sets fetchpriority=high and disables lazy loading for this image\n",[329,658,659],{"class":71,"line":433},[329,660,661],{"class":334},"\u002F>\n",[329,663,664,667],{"class":71,"line":440},[329,665,666],{"class":334},"{",[329,668,669],{"class":436},"\u002F* trade-off: priority should be set on at most one or two above-the-fold images.\n",[329,671,672],{"class":71,"line":446},[329,673,674],{"class":436},"    Setting it everywhere defeats the purpose and floods the network with eager\n",[329,676,678,681],{"class":71,"line":677},12,[329,679,680],{"class":436},"    requests, re-creating the bandwidth contention you are trying to remove. *\u002F",[329,682,620],{"class":334},[14,684,685],{},"Expected outcome: prevents recurrence across the app and fixes any other heroes silently deferred by the same default.",[235,687,689],{"id":688},"fix-4-for-custom-observers-never-defer-the-hero","Fix 4: For custom observers, never defer the hero",[14,691,692,693,695,696,698],{},"If a ",[184,694,279],{}," IntersectionObserver swallowed the hero (root cause 3), exclude the LCP element from the observer entirely and render it with a real ",[184,697,283],{},". Custom deferral should only ever apply below the fold.",[320,700,704],{"className":701,"code":702,"language":703,"meta":325,"style":325},"language-javascript shiki shiki-themes github-light-high-contrast github-light-high-contrast github-light-high-contrast","\u002F\u002F Skip the hero: only observe images explicitly marked deferrable\ndocument.querySelectorAll('img[data-src]:not([data-eager])').forEach((img) => io.observe(img));\n\u002F\u002F trade-off: relying on a class\u002Fattribute to exclude the hero is fragile if authors\n\u002F\u002F forget it. Safer still is to give the hero a normal src and never route it\n\u002F\u002F through the observer markup at all. -->\n","javascript",[184,705,706,711,753,758,763],{"__ignoreMap":325},[329,707,708],{"class":71,"line":331},[329,709,710],{"class":436},"\u002F\u002F Skip the hero: only observe images explicitly marked deferrable\n",[329,712,713,716,720,723,726,729,732,735,738,741,744,747,750],{"class":71,"line":342},[329,714,715],{"class":334},"document.",[329,717,719],{"class":718},"ssM3C","querySelectorAll",[329,721,722],{"class":334},"(",[329,724,725],{"class":352},"'img[data-src]:not([data-eager])'",[329,727,728],{"class":334},").",[329,730,731],{"class":718},"forEach",[329,733,734],{"class":334},"((",[329,736,41],{"class":737},"seIZK",[329,739,740],{"class":334},") ",[329,742,743],{"class":604},"=>",[329,745,746],{"class":334}," io.",[329,748,749],{"class":718},"observe",[329,751,752],{"class":334},"(img));\n",[329,754,755],{"class":71,"line":356},[329,756,757],{"class":436},"\u002F\u002F trade-off: relying on a class\u002Fattribute to exclude the hero is fragile if authors\n",[329,759,760],{"class":71,"line":367},[329,761,762],{"class":436},"\u002F\u002F forget it. Safer still is to give the hero a normal src and never route it\n",[329,764,765],{"class":71,"line":378},[329,766,767],{"class":436},"\u002F\u002F through the observer markup at all. -->\n",[14,769,770],{},"Expected outcome: removes script-execution latency from the LCP path, recovering the full deferral penalty plus the observer's own delay.",[235,772,774],{"id":773},"fix-5-preload-the-hero-when-discovery-is-genuinely-late","Fix 5: Preload the hero when discovery is genuinely late",[14,776,777],{},"If the hero is eager but injected by script or buried behind blocking CSS (root cause 4), declare it to the preload scanner directly so discovery does not wait for the DOM to settle.",[320,779,781],{"className":322,"code":780,"language":324,"meta":325,"style":325},"\u003Clink rel=\"preload\" as=\"image\"\n      href=\"hero-1200.jpg\"\n      imagesrcset=\"hero-800.jpg 800w, hero-1600.jpg 1600w\"\n      imagesizes=\"(max-width: 768px) 100vw, 1100px\"\n      fetchpriority=\"high\">\n\u003C!-- trade-off: a preload races every other early resource, so preloading a NON-LCP\n     image steals bandwidth from the real one. Preload only the confirmed LCP image,\n     and remove the hint if the layout changes and it is no longer the LCP element. -->\n",[184,782,783,806,815,824,833,844,849,854],{"__ignoreMap":325},[329,784,785,787,790,793,795,798,801,803],{"class":71,"line":331},[329,786,335],{"class":334},[329,788,789],{"class":338},"link",[329,791,792],{"class":345}," rel",[329,794,349],{"class":334},[329,796,797],{"class":352},"\"preload\"",[329,799,800],{"class":345}," as",[329,802,349],{"class":334},[329,804,805],{"class":352},"\"image\"\n",[329,807,808,811,813],{"class":71,"line":342},[329,809,810],{"class":345},"      href",[329,812,349],{"class":334},[329,814,353],{"class":352},[329,816,817,820,822],{"class":71,"line":356},[329,818,819],{"class":345},"      imagesrcset",[329,821,349],{"class":334},[329,823,494],{"class":352},[329,825,826,829,831],{"class":71,"line":367},[329,827,828],{"class":345},"      imagesizes",[329,830,349],{"class":334},[329,832,375],{"class":352},[329,834,835,838,840,842],{"class":71,"line":378},[329,836,837],{"class":345},"      fetchpriority",[329,839,349],{"class":334},[329,841,544],{"class":352},[329,843,430],{"class":334},[329,845,846],{"class":71,"line":397},[329,847,848],{"class":436},"\u003C!-- trade-off: a preload races every other early resource, so preloading a NON-LCP\n",[329,850,851],{"class":71,"line":408},[329,852,853],{"class":436},"     image steals bandwidth from the real one. Preload only the confirmed LCP image,\n",[329,855,856],{"class":71,"line":419},[329,857,858],{"class":436},"     and remove the hint if the layout changes and it is no longer the LCP element. -->\n",[14,860,861],{},"Expected outcome: surfaces the request at the very start of the load even when the element is added late, eliminating the discovery gap.",[160,863,865],{"id":864},"verification-prove-the-recovery","Verification: Prove the Recovery",[14,867,868,869,871,872,244,874,876],{},"Re-run the exact baseline from the diagnosis checklist and compare. The before\u002Fafter should show the hero's ",[184,870,186],{}," attribute changed from ",[184,873,93],{},[184,875,128],{},", the request initiator changed from layout\u002Fscript back to the preload scanner, and the request start time moved into the first wave. Confirm the LCP value itself: in the Performance trace, the LCP marker should sit back under 2.5s on the throttled mobile profile, with load delay collapsed.",[320,878,882],{"className":879,"code":880,"language":881,"meta":325,"style":325},"language-diff shiki shiki-themes github-light-high-contrast github-light-high-contrast github-light-high-contrast","- \u003Cimg src=\"hero-1200.jpg\" loading=\"lazy\" decoding=\"async\">\n+ \u003Cimg src=\"hero-1200.jpg\" loading=\"eager\" fetchpriority=\"high\" decoding=\"async\">\n","diff",[184,883,884,889],{"__ignoreMap":325},[329,885,886],{"class":71,"line":331},[329,887,888],{},"- \u003Cimg src=\"hero-1200.jpg\" loading=\"lazy\" decoding=\"async\">\n",[329,890,891],{"class":71,"line":342},[329,892,893],{},"+ \u003Cimg src=\"hero-1200.jpg\" loading=\"eager\" fetchpriority=\"high\" decoding=\"async\">\n",[14,895,896],{},"Then lock it in. Add a Lighthouse CI assertion so the hero can never be silently re-deferred, and watch field data — synthetic recovery should show up as a p75 LCP improvement in RUM within a collection window. Confirm the same change did not regress the below-the-fold deferral: initial image bytes should still be low because only the one hero became eager.",[320,898,902],{"className":899,"code":900,"language":901,"meta":325,"style":325},"language-json shiki shiki-themes github-light-high-contrast github-light-high-contrast github-light-high-contrast","{\n  \"ci\": {\n    \"assert\": {\n      \"assertions\": {\n        \"largest-contentful-paint\": [\"error\", { \"maxNumericValue\": 2500 }],\n        \"offscreen-images\": [\"error\", { \"minScore\": 0.9 }]\n      }\n    }\n  }\n}\n","json",[184,903,904,909,917,924,931,957,979,984,989,994],{"__ignoreMap":325},[329,905,906],{"class":71,"line":331},[329,907,908],{"class":334},"{\n",[329,910,911,914],{"class":71,"line":342},[329,912,913],{"class":338},"  \"ci\"",[329,915,916],{"class":334},": {\n",[329,918,919,922],{"class":71,"line":356},[329,920,921],{"class":338},"    \"assert\"",[329,923,916],{"class":334},[329,925,926,929],{"class":71,"line":367},[329,927,928],{"class":338},"      \"assertions\"",[329,930,916],{"class":334},[329,932,933,936,939,942,945,948,951,954],{"class":71,"line":378},[329,934,935],{"class":338},"        \"largest-contentful-paint\"",[329,937,938],{"class":334},": [",[329,940,941],{"class":352},"\"error\"",[329,943,944],{"class":334},", { ",[329,946,947],{"class":338},"\"maxNumericValue\"",[329,949,950],{"class":334},": ",[329,952,953],{"class":345},"2500",[329,955,956],{"class":334}," }],\n",[329,958,959,962,964,966,968,971,973,976],{"class":71,"line":397},[329,960,961],{"class":338},"        \"offscreen-images\"",[329,963,938],{"class":334},[329,965,941],{"class":352},[329,967,944],{"class":334},[329,969,970],{"class":338},"\"minScore\"",[329,972,950],{"class":334},[329,974,975],{"class":345},"0.9",[329,977,978],{"class":334}," }]\n",[329,980,981],{"class":71,"line":408},[329,982,983],{"class":334},"      }\n",[329,985,986],{"class":71,"line":419},[329,987,988],{"class":334},"    }\n",[329,990,991],{"class":71,"line":433},[329,992,993],{"class":334},"  }\n",[329,995,996],{"class":71,"line":440},[329,997,620],{"class":334},[14,999,1000],{},[206,1001,1002,1003,1006,1007,1010],{},"Assert ",[184,1004,1005],{},"largest-contentful-paint"," and ",[184,1008,1009],{},"offscreen-images"," together so the hero stays eager while everything else stays deferred.",[160,1012,1014],{"id":1013},"related","Related",[1016,1017,1018,1024,1030,1036,1043],"ul",{},[171,1019,1020,1023],{},[18,1021,1022],{"href":20},"Lazy loading images without hurting LCP"," — the full deferral workflow this recovery slots into.",[171,1025,1026,1029],{},[18,1027,1028],{"href":466},"Using fetchpriority to prioritize the LCP image"," — make the now-eager hero win the request queue.",[171,1031,1032,1035],{},[18,1033,1034],{"href":30},"Measuring LCP with Chrome DevTools"," — the trace workflow behind the diagnosis checklist.",[171,1037,1038,1042],{},[18,1039,1041],{"href":1040},"\u002Fimage-media-optimization\u002Flazy-loading-images-without-hurting-lcp\u002Fnative-lazy-loading-vs-intersection-observer\u002F","Native lazy loading vs IntersectionObserver"," — pick the deferral mechanism that will not swallow your hero again.",[171,1044,1045,1049],{},[18,1046,1048],{"href":1047},"\u002Fcore-web-vitals-measurement\u002Freducing-cumulative-layout-shift-cls\u002F","Reducing Cumulative Layout Shift"," — verify the eager hero still reserves its box and does not shift content.",[216,1051,1053],{"type":1052},"application\u002Fld+json","\n{\n  \"@context\": \"https:\u002F\u002Fschema.org\",\n  \"@type\": \"HowTo\",\n  \"name\": \"Fix a lazy-loaded image that delays LCP\",\n  \"description\": \"Diagnose and reverse an LCP regression caused by a lazily-loaded hero image with ordered fixes and verification.\",\n  \"step\": [\n    { \"@type\": \"HowToStep\", \"position\": 1, \"name\": \"Make the hero eager\", \"text\": \"Remove loading=lazy from the LCP image and set loading=eager so the preload scanner discovers it.\", \"url\": \"https:\u002F\u002Ffrontend-performance.com\u002Fimage-media-optimization\u002Flazy-loading-images-without-hurting-lcp\u002Ffixing-lazy-loaded-images-that-delay-lcp\u002F#fix-1-make-the-hero-eager\" },\n    { \"@type\": \"HowToStep\", \"position\": 2, \"name\": \"Add high fetch priority\", \"text\": \"Add fetchpriority=high to the hero so its request jumps the queue once discovered.\", \"url\": \"https:\u002F\u002Ffrontend-performance.com\u002Fimage-media-optimization\u002Flazy-loading-images-without-hurting-lcp\u002Ffixing-lazy-loaded-images-that-delay-lcp\u002F#fix-2-add-fetchpriorityhigh-to-the-hero\" },\n    { \"@type\": \"HowToStep\", \"position\": 3, \"name\": \"Fix the component default\", \"text\": \"Pass the priority prop on framework image components and audit other above-the-fold uses.\", \"url\": \"https:\u002F\u002Ffrontend-performance.com\u002Fimage-media-optimization\u002Flazy-loading-images-without-hurting-lcp\u002Ffixing-lazy-loaded-images-that-delay-lcp\u002F#fix-3-fix-the-component-default-not-just-the-instance\" },\n    { \"@type\": \"HowToStep\", \"position\": 4, \"name\": \"Exclude the hero from custom observers\", \"text\": \"Never route the LCP element through a data-src IntersectionObserver; render it with a real src.\", \"url\": \"https:\u002F\u002Ffrontend-performance.com\u002Fimage-media-optimization\u002Flazy-loading-images-without-hurting-lcp\u002Ffixing-lazy-loaded-images-that-delay-lcp\u002F#fix-4-for-custom-observers-never-defer-the-hero\" },\n    { \"@type\": \"HowToStep\", \"position\": 5, \"name\": \"Preload when discovery is late\", \"text\": \"Use a preload link with imagesrcset for heroes injected by script or buried behind blocking CSS.\", \"url\": \"https:\u002F\u002Ffrontend-performance.com\u002Fimage-media-optimization\u002Flazy-loading-images-without-hurting-lcp\u002Ffixing-lazy-loaded-images-that-delay-lcp\u002F#fix-5-preload-the-hero-when-discovery-is-genuinely-late\" }\n  ]\n}\n",[216,1055,1056],{"type":1052},"\n{\n  \"@context\": \"https:\u002F\u002Fschema.org\",\n  \"@type\": \"TechArticle\",\n  \"headline\": \"Fixing Lazy-Loaded Images That Delay LCP\",\n  \"description\": \"Diagnose and reverse an LCP regression caused by a lazily-loaded hero image, with ordered fixes and field verification.\",\n  \"datePublished\": \"2026-06-18\",\n  \"dateModified\": \"2026-06-18\",\n  \"author\": { \"@type\": \"Organization\", \"name\": \"frontend-performance.com\" },\n  \"publisher\": { \"@type\": \"Organization\", \"name\": \"frontend-performance.com\" },\n  \"mainEntityOfPage\": { \"@type\": \"WebPage\", \"@id\": \"https:\u002F\u002Ffrontend-performance.com\u002Fimage-media-optimization\u002Flazy-loading-images-without-hurting-lcp\u002Ffixing-lazy-loaded-images-that-delay-lcp\u002F\" }\n}\n",[216,1058,1059],{"type":1052},"\n{\n  \"@context\": \"https:\u002F\u002Fschema.org\",\n  \"@type\": \"BreadcrumbList\",\n  \"itemListElement\": [\n    { \"@type\": \"ListItem\", \"position\": 1, \"name\": \"Home\", \"item\": \"https:\u002F\u002Ffrontend-performance.com\u002F\" },\n    { \"@type\": \"ListItem\", \"position\": 2, \"name\": \"Image & Media Optimization\", \"item\": \"https:\u002F\u002Ffrontend-performance.com\u002Fimage-media-optimization\u002F\" },\n    { \"@type\": \"ListItem\", \"position\": 3, \"name\": \"Lazy Loading Images Without Hurting LCP\", \"item\": \"https:\u002F\u002Ffrontend-performance.com\u002Fimage-media-optimization\u002Flazy-loading-images-without-hurting-lcp\u002F\" },\n    { \"@type\": \"ListItem\", \"position\": 4, \"name\": \"Fixing Lazy-Loaded Images That Delay LCP\", \"item\": \"https:\u002F\u002Ffrontend-performance.com\u002Fimage-media-optimization\u002Flazy-loading-images-without-hurting-lcp\u002Ffixing-lazy-loaded-images-that-delay-lcp\u002F\" }\n  ]\n}\n",[1061,1062,1063],"style",{},"html pre.shiki code .syybb, html code.shiki .syybb{--shiki-default:#0E1116;--shiki-dark:#0E1116;--shiki-light:#0E1116}html pre.shiki code .s-fAs, html code.shiki .s-fAs{--shiki-default:#024C1A;--shiki-dark:#024C1A;--shiki-light:#024C1A}html pre.shiki code .sf6mN, html code.shiki .sf6mN{--shiki-default:#023B95;--shiki-dark:#023B95;--shiki-light:#023B95}html pre.shiki code .s-_DF, html code.shiki .s-_DF{--shiki-default:#032563;--shiki-dark:#032563;--shiki-light:#032563}html pre.shiki code .sIIH1, html code.shiki .sIIH1{--shiki-default:#66707B;--shiki-dark:#66707B;--shiki-light:#66707B}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html pre.shiki code .sP5qI, html code.shiki .sP5qI{--shiki-default:#A0111F;--shiki-dark:#A0111F;--shiki-light:#A0111F}html pre.shiki code .ssM3C, html code.shiki .ssM3C{--shiki-default:#622CBC;--shiki-dark:#622CBC;--shiki-light:#622CBC}html pre.shiki code .seIZK, html code.shiki .seIZK{--shiki-default:#702C00;--shiki-dark:#702C00;--shiki-light:#702C00}",{"title":325,"searchDepth":342,"depth":342,"links":1065},[1066,1067,1073,1080,1081],{"id":162,"depth":342,"text":163},{"id":232,"depth":342,"text":233,"children":1068},[1069,1070,1071,1072],{"id":237,"depth":356,"text":238},{"id":253,"depth":356,"text":254},{"id":272,"depth":356,"text":273},{"id":290,"depth":356,"text":291},{"id":304,"depth":342,"text":305,"children":1074},[1075,1076,1077,1078,1079],{"id":308,"depth":356,"text":309},{"id":455,"depth":356,"text":456},{"id":574,"depth":356,"text":575},{"id":688,"depth":356,"text":689},{"id":773,"depth":356,"text":774},{"id":864,"depth":342,"text":865},{"id":1013,"depth":342,"text":1014},"A diagnostic playbook for an LCP regression caused by a lazily-loaded hero image: root causes, ordered fixes, and verification.","md",{"slug":12,"type":1085,"breadcrumb":1086,"datePublished":1095,"dateModified":1095},"long_tail",[1087,1090,1091,1093],{"name":1088,"url":1089},"Home","\u002F",{"name":26,"url":25},{"name":1092,"url":20},"Lazy Loading Images Without Hurting LCP",{"name":5,"url":1094},"\u002Fimage-media-optimization\u002Flazy-loading-images-without-hurting-lcp\u002Ffixing-lazy-loaded-images-that-delay-lcp\u002F","2026-06-18",true,"\u002Fimage-media-optimization\u002Flazy-loading-images-without-hurting-lcp\u002Ffixing-lazy-loaded-images-that-delay-lcp",{"title":5,"description":1099},"Your LCP regressed after enabling lazy loading because the hero image was deferred. Diagnose the cause, apply ordered fixes, and verify the recovery.","image-media-optimization\u002Flazy-loading-images-without-hurting-lcp\u002Ffixing-lazy-loaded-images-that-delay-lcp\u002Findex","U8arsMSHSxSuDw8cq0AI6Zs5JdOSlKUTrCHWUsiWCWY",[1103,1106],{"title":1092,"path":1104,"stem":1105,"children":-1},"\u002Fimage-media-optimization\u002Flazy-loading-images-without-hurting-lcp","image-media-optimization\u002Flazy-loading-images-without-hurting-lcp\u002Findex",{"title":1107,"path":1108,"stem":1109,"children":-1},"Native Lazy Loading vs IntersectionObserver","\u002Fimage-media-optimization\u002Flazy-loading-images-without-hurting-lcp\u002Fnative-lazy-loading-vs-intersection-observer","image-media-optimization\u002Flazy-loading-images-without-hurting-lcp\u002Fnative-lazy-loading-vs-intersection-observer\u002Findex",1782237171323]