[{"data":1,"prerenderedAt":1759},["ShallowReactive",2],{"content:\u002Fimage-media-optimization\u002Fimage-decoding-and-placeholders":3,"surroundings:\u002Fimage-media-optimization\u002Fimage-decoding-and-placeholders":1750},{"id":4,"title":5,"body":6,"description":1731,"extension":1732,"meta":1733,"navigation":1743,"path":1744,"seo":1745,"stem":1748,"__hash__":1749},"content\u002Fimage-media-optimization\u002Fimage-decoding-and-placeholders\u002Findex.md","Image Decoding & Placeholders",{"type":7,"value":8,"toc":1709},"minimark",[9,14,24,40,188,193,196,229,310,314,352,356,359,363,366,370,373,499,503,559,563,643,742,746,756,762,768,774,780,784,787,1012,1016,1108,1219,1223,1229,1233,1241,1245,1305,1449,1453,1456,1460,1466,1470,1517,1521,1528,1532,1544,1553,1562,1571,1580,1589,1598,1614,1623,1632,1636,1660,1664,1694,1699,1702,1705],[10,11,13],"h1",{"id":12},"image-decoding-placeholders-from-bytes-to-pixels","Image Decoding & Placeholders: From Bytes to Pixels",[15,16,17,18,23],"p",{},"This topic extends ",[19,20,22],"a",{"href":21},"\u002Fimage-media-optimization\u002F","Image & Media Optimization"," to the part of image delivery that happens after the network. When an image's bytes arrive, the browser still has to decode them — decompress the AVIF, WebP or JPEG into a bitmap — before it can paint. For a full-screen photo on a high-density phone, that bitmap can be 12 million pixels, and decoding it takes tens of milliseconds on mid-tier hardware, more for AVIF at high resolutions. Where and when that decode happens decides whether it delays LCP, blocks interactions, or happens invisibly in the background.",[15,25,26,27,31,32,35,36,39],{},"The same window — bytes requested, bytes arriving, pixels painted — is what placeholders exist to cover. A dominant-colour block, a tiny blurred preview (LQIP), or a BlurHash string can make a page feel complete while full images load, and when sized correctly they also prevent layout shift. Done badly, placeholders add bytes and JavaScript to the critical path, or even become the LCP element themselves. The thresholds remain LCP under ",[28,29,30],"code",{},"2.5s",", INP under ",[28,33,34],{},"200ms"," and CLS under ",[28,37,38],{},"0.1",".",[15,41,42],{},[43,44,50,51,50,58,50,62,50,65,50,83,50,91,50,97,50,105,50,112,50,117,50,121,50,124,50,128,50,131,50,134,50,140,50,144,50,147,50,150,50,154,50,157,50,160,50,164,50,167,50,176,50,180,50,184,50],"svg",{"viewBox":45,"width":46,"role":47,"ariaLabel":48,"style":49},"0 0 760 149","100%","img","Stages from the image request through download, decode and paint, with the placeholder covering the gap.","height:auto;max-width:760px;display:block;margin:1.75rem auto;font-family:inherit;color:var(--fp-svg-ink)"," ",[52,53],"rect",{"className":54,"x":56,"y":56,"width":46,"height":46,"fill":57},[55],"svg-canvas","0","#ffffff",[59,60,61],"title",{},"An image's path from bytes to pixels",[63,64,48],"desc",{},[66,67,68],"defs",{},[69,70,77],"marker",{"id":71,"viewBox":72,"refX":73,"refY":74,"markerWidth":75,"markerHeight":75,"orient":76},"fac95e1e82","0 0 10 10","9","5","7","auto-start-reverse",[78,79],"path",{"d":80,"fill":81,"style":82},"M0 0 L10 5 L0 10 z","currentColor","fill-opacity:0.7",[52,84],{"x":85,"y":85,"width":86,"height":87,"rx":88,"fill":89,"stroke":81,"style":90},"1","758","147","10","none","stroke-opacity:0.18",[92,93,61],"text",{"x":94,"y":95,"fill":81,"style":96},"28.0","34.0","font-size:16px;font-weight:700",[52,98],{"x":94,"y":99,"width":100,"height":101,"rx":102,"fill":103,"stroke":103,"style":104},"56.0","108.8","71.0","6","#0466c8","fill-opacity:0.14;stroke-opacity:0.9",[92,106,111],{"x":107,"y":108,"fill":81,"style":109,"textAnchor":110},"82.4","78.0","font-size:13px;font-weight:700","middle","Request",[92,113,116],{"x":107,"y":114,"fill":81,"style":115,"textAnchor":110},"96.0","font-size:12px","discovered +",[92,118,120],{"x":107,"y":119,"fill":81,"style":115,"textAnchor":110},"112.0","prioritised",[52,122],{"x":123,"y":99,"width":100,"height":101,"rx":102,"fill":103,"stroke":103,"style":104},"176.8",[92,125,127],{"x":126,"y":108,"fill":81,"style":109,"textAnchor":110},"231.2","Download",[92,129,130],{"x":126,"y":114,"fill":81,"style":115,"textAnchor":110},"bytes over",[92,132,133],{"x":126,"y":119,"fill":81,"style":115,"textAnchor":110},"network",[52,135],{"x":136,"y":99,"width":100,"height":101,"rx":102,"fill":137,"stroke":138,"style":139},"325.6","#ffc300","#b8860b","fill-opacity:0.24;stroke-opacity:0.9",[92,141,143],{"x":142,"y":108,"fill":81,"style":109,"textAnchor":110},"380.0","Decode",[92,145,146],{"x":142,"y":114,"fill":81,"style":115,"textAnchor":110},"bytes → bitmap",[52,148],{"x":149,"y":99,"width":100,"height":101,"rx":102,"fill":103,"stroke":103,"style":104},"474.4",[92,151,153],{"x":152,"y":108,"fill":81,"style":109,"textAnchor":110},"528.8","Paint",[92,155,156],{"x":152,"y":114,"fill":81,"style":115,"textAnchor":110},"LCP recorded",[52,158],{"x":159,"y":99,"width":100,"height":101,"rx":102,"fill":103,"stroke":103,"style":104},"623.2",[92,161,163],{"x":162,"y":108,"fill":81,"style":109,"textAnchor":110},"677.6","Placeholder",[92,165,166],{"x":162,"y":114,"fill":81,"style":115,"textAnchor":110},"covers until paint",[168,169],"line",{"x1":170,"y1":171,"x2":172,"y2":171,"stroke":81,"strokeWidth":173,"style":174,"markerEnd":175},"136.8","91.5","174.8","1.5","stroke-opacity:0.6","url(#fac95e1e82)",[168,177],{"x1":178,"y1":171,"x2":179,"y2":171,"stroke":81,"strokeWidth":173,"style":174,"markerEnd":175},"285.6","323.6",[168,181],{"x1":182,"y1":171,"x2":183,"y2":171,"stroke":81,"strokeWidth":173,"style":174,"markerEnd":175},"434.4","472.4",[168,185],{"x1":186,"y1":171,"x2":187,"y2":171,"stroke":81,"strokeWidth":173,"style":174,"markerEnd":175},"583.2","621.2",[189,190,192],"h2",{"id":191},"the-degradation-this-topic-addresses","The Degradation This Topic Addresses",[15,194,195],{},"Decode and placeholder problems surface as:",[197,198,199,207,213,223],"ul",{},[200,201,202,206],"li",{},[203,204,205],"strong",{},"LCP render delay with the image already downloaded."," The network waterfall shows the LCP image finished early, yet LCP is later; the gap is decode and paint, sometimes on a busy main thread.",[200,208,209,212],{},[203,210,211],{},"Janky scrolling and slow interactions on image-heavy pages."," Large images decoded synchronously during scroll or after interactions add long tasks.",[200,214,215,218,219,222],{},[203,216,217],{},"CSS background heroes discovered late."," A hero set via ",[28,220,221],{},"background-image"," cannot be found by the preload scanner, so its request — and therefore LCP — starts late.",[200,224,225,228],{},[203,226,227],{},"Placeholders that cost more than they save."," Base64 previews inflating HTML, BlurHash decoders in the startup bundle, or placeholders that shift when the real image arrives.",[15,230,231],{},[43,232,50,235,50,238,50,241,50,243,50,246,50,248,50,255,50,262,50,267,50,271,50,275,50,279,50,283,50,288,50,292,50,296,50,300,50,304,50],{"viewBox":233,"width":46,"role":47,"ariaLabel":234,"style":49},"0 0 760 220","Bar chart of decode time for the same hero photo at different resolutions and formats on a mid-tier phone.",[52,236],{"className":237,"x":56,"y":56,"width":46,"height":46,"fill":57},[55],[59,239,240],{},"Decode time for one hero image on a mid-tier phone",[63,242,234],{},[52,244],{"x":85,"y":85,"width":86,"height":245,"rx":88,"fill":89,"stroke":81,"style":90},"218",[92,247,240],{"x":94,"y":95,"fill":81,"style":96},[92,249,254],{"x":250,"y":251,"fill":81,"style":252,"textAnchor":253},"169.7","70.0","font-size:13px","end","JPEG 1080w",[52,256],{"x":257,"y":99,"width":258,"height":259,"rx":260,"fill":103,"stroke":103,"style":261},"181.7","71.8","19","3","fill-opacity:0.55;stroke-opacity:0.9",[92,263,266],{"x":264,"y":251,"fill":81,"style":265},"259.4","font-size:12px;font-weight:600","9ms",[92,268,270],{"x":250,"y":269,"fill":81,"style":252,"textAnchor":253},"101.0","AVIF 1080w",[52,272],{"x":257,"y":273,"width":274,"height":259,"rx":260,"fill":103,"stroke":103,"style":261},"87.0","111.6",[92,276,278],{"x":277,"y":269,"fill":81,"style":265},"299.3","14ms",[92,280,282],{"x":250,"y":281,"fill":81,"style":252,"textAnchor":253},"132.0","JPEG 2400w (oversized)",[52,284],{"x":257,"y":285,"width":286,"height":259,"rx":260,"fill":137,"stroke":138,"style":287},"118.0","303.0","fill-opacity:0.7;stroke-opacity:0.9",[92,289,291],{"x":290,"y":281,"fill":81,"style":265},"490.6","38ms",[92,293,295],{"x":250,"y":294,"fill":81,"style":252,"textAnchor":253},"163.0","AVIF 2400w (oversized)",[52,297],{"x":257,"y":298,"width":299,"height":259,"rx":260,"fill":137,"stroke":138,"style":287},"149.0","486.3",[92,301,303],{"x":302,"y":294,"fill":81,"style":265},"674.0","61ms",[92,305,309],{"x":94,"y":306,"fill":307,"style":308},"198.0","#51617a","font-size:12.5px","Oversized images cost decode time as well as bytes; serving the displayed size is the biggest decode optimisation.",[189,311,313],{"id":312},"prerequisites","Prerequisites",[197,315,316,322,332,338],{},[200,317,318,321],{},[203,319,320],{},"DevTools Performance panel"," with screenshots, to see \"Image Decode\" events and their thread.",[200,323,324,327,328,331],{},[203,325,326],{},"LCP phase attribution"," from the ",[28,329,330],{},"web-vitals"," attribution build (render delay in particular).",[200,333,334,337],{},[203,335,336],{},"An image pipeline"," that can generate responsive sizes and tiny placeholders at build time or via an image CDN.",[200,339,340,343,344,347,348,351],{},[203,341,342],{},"Knowledge of where heroes are defined"," — ",[28,345,346],{},"\u003Cimg>",", ",[28,349,350],{},"\u003Cpicture>",", or CSS backgrounds.",[189,353,355],{"id":354},"_1-environment-setup-make-decode-visible","1. Environment Setup: Make Decode Visible",[15,357,358],{},"Record a throttled trace (4x CPU) of a hero-image page. In the Main and Raster threads, look for Image Decode events; note their duration and whether they occur on the main thread before the LCP paint.",[189,360,362],{"id":361},"_2-capture-a-baseline","2. Capture a Baseline",[15,364,365],{},"From field data, take LCP render delay p75 for image-LCP templates. From the lab, record decode duration for the hero, bytes of placeholder data inlined in HTML, and whether any placeholder library runs at startup.",[189,367,369],{"id":368},"_3-isolate-the-bottleneck","3. Isolate the Bottleneck",[15,371,372],{},"If render delay is large and the trace shows long decodes, the image is oversized or decoding synchronously. If load delay is large and the hero is a CSS background, discovery is the issue. If CLS is attributed to image containers, placeholder sizing is the issue.",[15,374,375],{},[43,376,50,379,50,382,50,385,50,387,50,394,50,397,50,399,50,404,50,409,50,412,50,417,50,421,50,427,50,432,50,436,50,441,50,445,50,449,50,451,50,454,50,457,50,460,50,464,50,467,50,470,50,474,50,476,50,479,50,482,50,485,50,489,50,492,50,495,50],{"viewBox":377,"width":46,"role":47,"ariaLabel":378,"style":49},"0 0 760 340","Decision sequence for identifying whether decoding, discovery or placeholder sizing is the problem for an image.",[52,380],{"className":381,"x":56,"y":56,"width":46,"height":46,"fill":57},[55],[59,383,384],{},"Diagnosing the gap between bytes and pixels",[63,386,378],{},[66,388,389],{},[69,390,392],{"id":391,"viewBox":72,"refX":73,"refY":74,"markerWidth":75,"markerHeight":75,"orient":76},"fa7ce5f030",[78,393],{"d":80,"fill":81,"style":82},[52,395],{"x":85,"y":85,"width":86,"height":396,"rx":88,"fill":89,"stroke":81,"style":90},"338",[92,398,384],{"x":94,"y":95,"fill":81,"style":96},[52,400],{"x":94,"y":99,"width":401,"height":402,"rx":102,"fill":81,"stroke":81,"style":403},"320.0","50.0","fill-opacity:0.06;stroke-opacity:0.4",[92,405,408],{"x":406,"y":407,"fill":81,"style":109,"textAnchor":110},"188.0","85.5","Image downloaded early but LCP late?",[52,410],{"x":411,"y":99,"width":401,"height":402,"rx":102,"fill":103,"stroke":103,"style":104},"412.0",[92,413,416],{"x":414,"y":415,"fill":81,"style":308,"textAnchor":110},"572.0","77.5","Decode or render delay — right-size and decode",[92,418,420],{"x":414,"y":419,"fill":81,"style":308,"textAnchor":110},"93.5","async",[168,422],{"x1":423,"y1":424,"x2":425,"y2":424,"stroke":81,"strokeWidth":173,"style":174,"markerEnd":426},"348.0","81.0","410.0","url(#fa7ce5f030)",[92,428,431],{"x":142,"y":429,"fill":103,"style":430,"textAnchor":110},"74.0","font-size:11.5px;font-weight:700","yes",[168,433],{"x1":406,"y1":434,"x2":406,"y2":435,"stroke":81,"strokeWidth":173,"style":174,"markerEnd":426},"106.0","140.0",[92,437,440],{"x":438,"y":439,"fill":307,"style":430},"196.0","128.0","no",[52,442],{"x":94,"y":443,"width":401,"height":444,"rx":102,"fill":81,"stroke":81,"style":403},"142.0","35.0",[92,446,448],{"x":406,"y":447,"fill":81,"style":109,"textAnchor":110},"164.0","Hero is a CSS background-image?",[52,450],{"x":411,"y":443,"width":401,"height":444,"rx":102,"fill":103,"stroke":103,"style":104},[92,452,453],{"x":414,"y":447,"fill":81,"style":308,"textAnchor":110},"Late discovery — move to img or preload",[168,455],{"x1":423,"y1":456,"x2":425,"y2":456,"stroke":81,"strokeWidth":173,"style":174,"markerEnd":426},"159.5",[92,458,431],{"x":142,"y":459,"fill":103,"style":430,"textAnchor":110},"152.5",[168,461],{"x1":406,"y1":462,"x2":406,"y2":463,"stroke":81,"strokeWidth":173,"style":174,"markerEnd":426},"177.0","211.0",[92,465,440],{"x":438,"y":466,"fill":307,"style":430},"199.0",[52,468],{"x":94,"y":469,"width":401,"height":444,"rx":102,"fill":81,"stroke":81,"style":403},"213.0",[92,471,473],{"x":406,"y":472,"fill":81,"style":109,"textAnchor":110},"235.0","Layout shifts when images arrive?",[52,475],{"x":411,"y":469,"width":401,"height":444,"rx":102,"fill":103,"stroke":103,"style":104},[92,477,478],{"x":414,"y":472,"fill":81,"style":308,"textAnchor":110},"Reserve space — sized placeholders",[168,480],{"x1":423,"y1":481,"x2":425,"y2":481,"stroke":81,"strokeWidth":173,"style":174,"markerEnd":426},"230.5",[92,483,431],{"x":142,"y":484,"fill":103,"style":430,"textAnchor":110},"223.5",[168,486],{"x1":406,"y1":487,"x2":406,"y2":488,"stroke":81,"strokeWidth":173,"style":174,"markerEnd":426},"248.0","282.0",[92,490,440],{"x":438,"y":491,"fill":307,"style":430},"270.0",[52,493],{"x":94,"y":494,"width":401,"height":95,"rx":102,"fill":137,"stroke":138,"style":139},"284.0",[92,496,498],{"x":406,"y":497,"fill":81,"style":308,"textAnchor":110},"305.5","Check placeholder cost on the critical path",[189,500,502],{"id":501},"_4-apply-the-fixes","4. Apply the Fixes",[504,505,506,512,529,538,550],"ol",{},[200,507,508,511],{},[203,509,510],{},"Right-size images"," so decode work matches the display — the largest decode saving.",[200,513,514,520,521,524,525,39],{},[203,515,516,517],{},"Use ",[28,518,519],{},"decoding=\"async\""," for non-LCP images and ",[28,522,523],{},"img.decode()"," before inserting images that appear on interaction — see ",[19,526,528],{"href":527},"\u002Fimage-media-optimization\u002Fimage-decoding-and-placeholders\u002Fusing-decoding-async-and-the-decode-api\u002F","using decoding=async and the decode() API",[200,530,531,343,534,39],{},[203,532,533],{},"Keep decode off the critical interaction path",[19,535,537],{"href":536},"\u002Fimage-media-optimization\u002Fimage-decoding-and-placeholders\u002Favoiding-main-thread-image-decode-jank\u002F","avoiding main-thread image decode jank",[200,539,540,545,546,39],{},[203,541,542,543],{},"Move CSS background heroes into ",[28,544,346],{}," or preload them — ",[19,547,549],{"href":548},"\u002Fimage-media-optimization\u002Fimage-decoding-and-placeholders\u002Fcss-background-images-as-lcp-elements\u002F","CSS background images as LCP elements",[200,551,552,343,555,39],{},[203,553,554],{},"Choose placeholders by cost",[19,556,558],{"href":557},"\u002Fimage-media-optimization\u002Fimage-decoding-and-placeholders\u002Flqip-vs-blurhash-placeholders\u002F","LQIP vs BlurHash placeholders",[189,560,562],{"id":561},"deconstructing-render-delay-for-an-image-lcp","Deconstructing Render Delay for an Image LCP",[564,565,566,585],"table",{},[567,568,569],"thead",{},[570,571,572,576,579,582],"tr",{},[573,574,575],"th",{},"Phase",[573,577,578],{},"What happens",[573,580,581],{},"Budget (mobile p75)",[573,583,584],{},"Lever",[586,587,588,603,616,629],"tbody",{},[570,589,590,594,597,600],{},[591,592,593],"td",{},"Load end → decode start",[591,595,596],{},"bitmap decode scheduled",[591,598,599],{},"\u003C 20ms",[591,601,602],{},"main thread free, async decode",[570,604,605,607,610,613],{},[591,606,143],{},[591,608,609],{},"bytes → pixels",[591,611,612],{},"\u003C 30ms",[591,614,615],{},"right-sized image, format",[570,617,618,621,624,626],{},[591,619,620],{},"Paint + raster",[591,622,623],{},"bitmap drawn to layers",[591,625,599],{},[591,627,628],{},"no expensive effects on the image",[570,630,631,634,637,640],{},[591,632,633],{},"Compositing",[591,635,636],{},"frame presented",[591,638,639],{},"\u003C 10ms",[591,641,642],{},"avoid huge layers",[15,644,645],{},[43,646,50,649,50,652,50,655,50,657,50,660,50,662,50,666,50,671,50,677,50,681,50,685,50,689,50,693,50,696,50,700,50,704,50,708,50,714,50,719,50,723,50,727,50,731,50,734,50,738,50],{"viewBox":647,"width":46,"role":47,"ariaLabel":648,"style":49},"0 0 760 178","Timelines comparing the time after a hero image finishes downloading until it paints, for an oversized image on a busy main thread and a right-sized one.",[52,650],{"className":651,"x":56,"y":56,"width":46,"height":46,"fill":57},[55],[59,653,654],{},"Render delay for a hero image, before and after right-sizing",[63,656,648],{},[52,658],{"x":85,"y":85,"width":86,"height":659,"rx":88,"fill":89,"stroke":81,"style":90},"176",[92,661,654],{"x":94,"y":95,"fill":81,"style":96},[92,663,665],{"x":94,"y":429,"fill":81,"style":664},"font-size:12.5px;font-weight:700","Before",[52,667],{"x":668,"y":99,"width":669,"height":670,"rx":260,"fill":81,"stroke":81,"style":403},"82.6","191.8","26.0",[92,672,676],{"x":673,"y":674,"fill":81,"style":675,"textAnchor":110},"178.4","73.5","font-size:11.5px","main thread busy",[52,678],{"x":679,"y":99,"width":680,"height":670,"rx":260,"fill":137,"stroke":138,"style":139},"275.8","148.8",[92,682,684],{"x":683,"y":674,"fill":81,"style":675,"textAnchor":110},"350.2","decode 2400w",[52,686],{"x":687,"y":99,"width":688,"height":670,"rx":260,"fill":81,"stroke":81,"style":403},"426.1","62.9",[92,690,692],{"x":691,"y":674,"fill":81,"style":675,"textAnchor":110},"457.6","paint",[92,694,695],{"x":94,"y":119,"fill":81,"style":664},"After",[52,697],{"x":668,"y":698,"width":699,"height":670,"rx":260,"fill":81,"stroke":81,"style":403},"94.0","30.7",[52,701],{"x":702,"y":698,"width":703,"height":670,"rx":260,"fill":81,"stroke":81,"style":403},"114.8","41.4",[92,705,692],{"x":706,"y":707,"fill":81,"style":675,"textAnchor":110},"135.5","111.5",[168,709],{"x1":710,"y1":711,"x2":712,"y2":711,"stroke":81,"style":713},"81.8","134.0","726.0","stroke-opacity:0.4",[92,715,718],{"x":716,"y":717,"fill":307,"style":675,"textAnchor":110},"92.5","151.0","0ms",[92,720,722],{"x":721,"y":717,"fill":307,"style":675,"textAnchor":110},"189.2","50ms",[92,724,726],{"x":725,"y":717,"fill":307,"style":675,"textAnchor":110},"296.5","100ms",[92,728,730],{"x":729,"y":717,"fill":307,"style":675,"textAnchor":110},"403.9","150ms",[92,732,34],{"x":733,"y":717,"fill":307,"style":675,"textAnchor":110},"511.3",[92,735,737],{"x":736,"y":717,"fill":307,"style":675,"textAnchor":110},"618.6","250ms",[92,739,741],{"x":740,"y":717,"fill":307,"style":675,"textAnchor":110},"715.3","300ms",[189,743,745],{"id":744},"advanced-diagnostics-and-edge-cases","Advanced Diagnostics and Edge Cases",[15,747,748,751,752,755],{},[203,749,750],{},"Decode on the main thread."," Chromium normally decodes images off the main thread, but some paths (synchronous decodes for images inserted and painted in the same frame, very large images, canvas ",[28,753,754],{},"drawImage"," of undecoded images) can block it. Long Image Decode events on the main thread are the tell.",[15,757,758,761],{},[203,759,760],{},"AVIF decode cost."," AVIF's smaller files come with heavier decoding than JPEG, especially at large sizes. For huge images on low-end devices, the byte saving can be partly offset; serving the right size keeps both small.",[15,763,764,767],{},[203,765,766],{},"Progressive JPEG."," Progressive JPEGs paint a low-quality version early; LCP is recorded when the first meaningful paint of the element occurs, which can improve perceived performance.",[15,769,770,773],{},[203,771,772],{},"Image elements removed and re-added."," Re-rendering image elements (common in client frameworks) can trigger repeated decodes; keep image nodes stable.",[15,775,776,779],{},[203,777,778],{},"Memory pressure."," Large decoded bitmaps consume memory; too many on one page can cause re-decoding when the browser discards them, adding jank on scroll.",[189,781,783],{"id":782},"validation-and-budgeting","Validation and Budgeting",[15,785,786],{},"Budget image dimensions against display size (no image more than ~1.5x its rendered width times DPR), track LCP render delay p75 in RUM, and keep placeholder bytes in HTML under a few kilobytes per page.",[788,789,794],"pre",{"className":790,"code":791,"language":792,"meta":793,"style":793},"language-javascript shiki shiki-themes github-light-high-contrast github-dark-high-contrast github-light-high-contrast","\u002F\u002F Lab check: flag images decoded at far more pixels than displayed.\nconst oversized = [...document.images].filter((img) => {\n  const r = img.getBoundingClientRect(); const dpr = devicePixelRatio;\n  return img.naturalWidth > r.width * dpr * 1.5 && r.width > 0;\n}).map((img) => `${img.currentSrc} ${img.naturalWidth}px for ${Math.round(img.getBoundingClientRect().width)}css px`);\nconsole.table(oversized);\n\u002F\u002F trade-off: this checks the current viewport and DPR only; run it at your\n\u002F\u002F main breakpoints and on a high-DPR profile.\n","javascript","",[28,795,796,804,847,877,916,989,1000,1006],{"__ignoreMap":793},[797,798,800],"span",{"class":168,"line":799},1,[797,801,803],{"class":802},"sjfSM","\u002F\u002F Lab check: flag images decoded at far more pixels than displayed.\n",[797,805,807,811,815,818,822,825,828,832,835,838,841,844],{"class":168,"line":806},2,[797,808,810],{"class":809},"sPARh","const",[797,812,814],{"class":813},"sPXB4"," oversized",[797,816,817],{"class":809}," =",[797,819,821],{"class":820},"saISM"," [",[797,823,824],{"class":809},"...",[797,826,827],{"class":820},"document.images].",[797,829,831],{"class":830},"smZ65","filter",[797,833,834],{"class":820},"((",[797,836,47],{"class":837},"sQw3B",[797,839,840],{"class":820},") ",[797,842,843],{"class":809},"=>",[797,845,846],{"class":820}," {\n",[797,848,850,853,856,858,861,864,867,869,872,874],{"class":168,"line":849},3,[797,851,852],{"class":809},"  const",[797,854,855],{"class":813}," r",[797,857,817],{"class":809},[797,859,860],{"class":820}," img.",[797,862,863],{"class":830},"getBoundingClientRect",[797,865,866],{"class":820},"(); ",[797,868,810],{"class":809},[797,870,871],{"class":813}," dpr",[797,873,817],{"class":809},[797,875,876],{"class":820}," devicePixelRatio;\n",[797,878,880,883,886,889,892,895,898,900,903,906,908,910,913],{"class":168,"line":879},4,[797,881,882],{"class":809},"  return",[797,884,885],{"class":820}," img.naturalWidth ",[797,887,888],{"class":809},">",[797,890,891],{"class":820}," r.width ",[797,893,894],{"class":809},"*",[797,896,897],{"class":820}," dpr ",[797,899,894],{"class":809},[797,901,902],{"class":813}," 1.5",[797,904,905],{"class":809}," &&",[797,907,891],{"class":820},[797,909,888],{"class":809},[797,911,912],{"class":813}," 0",[797,914,915],{"class":820},";\n",[797,917,919,922,925,927,929,931,933,937,939,941,944,947,949,951,954,957,960,962,965,968,970,972,974,977,980,983,986],{"class":168,"line":918},5,[797,920,921],{"class":820},"}).",[797,923,924],{"class":830},"map",[797,926,834],{"class":820},[797,928,47],{"class":837},[797,930,840],{"class":820},[797,932,843],{"class":809},[797,934,936],{"class":935},"sZ8jY"," `${",[797,938,47],{"class":820},[797,940,39],{"class":935},[797,942,943],{"class":820},"currentSrc",[797,945,946],{"class":935},"} ${",[797,948,47],{"class":820},[797,950,39],{"class":935},[797,952,953],{"class":820},"naturalWidth",[797,955,956],{"class":935},"}px for ${",[797,958,959],{"class":820},"Math",[797,961,39],{"class":935},[797,963,964],{"class":830},"round",[797,966,967],{"class":935},"(",[797,969,47],{"class":820},[797,971,39],{"class":935},[797,973,863],{"class":830},[797,975,976],{"class":935},"().",[797,978,979],{"class":820},"width",[797,981,982],{"class":935},")",[797,984,985],{"class":935},"}css px`",[797,987,988],{"class":820},");\n",[797,990,992,995,997],{"class":168,"line":991},6,[797,993,994],{"class":820},"console.",[797,996,564],{"class":830},[797,998,999],{"class":820},"(oversized);\n",[797,1001,1003],{"class":168,"line":1002},7,[797,1004,1005],{"class":802},"\u002F\u002F trade-off: this checks the current viewport and DPR only; run it at your\n",[797,1007,1009],{"class":168,"line":1008},8,[797,1010,1011],{"class":802},"\u002F\u002F main breakpoints and on a high-DPR profile.\n",[189,1013,1015],{"id":1014},"code-examples","Code Examples",[788,1017,1021],{"className":1018,"code":1019,"language":1020,"meta":793,"style":793},"language-html shiki shiki-themes github-light-high-contrast github-dark-high-contrast github-light-high-contrast","\u003C!-- LCP hero: eager, high priority, sized; decoding left to the browser (sync is fine for LCP). -->\n\u003Cimg src=\"\u002Fimg\u002Fhero-1200.avif\" srcset=\"\u002Fimg\u002Fhero-800.avif 800w, \u002Fimg\u002Fhero-1200.avif 1200w\"\n     sizes=\"100vw\" width=\"1200\" height=\"675\" fetchpriority=\"high\" alt=\"Trail runner on a ridge\">\n\u003C!-- trade-off: forcing decoding=\"async\" on the LCP image can delay its paint by\n     a frame in some browsers; leave the default for the hero. -->\n","html",[28,1022,1023,1028,1053,1098,1103],{"__ignoreMap":793},[797,1024,1025],{"class":168,"line":799},[797,1026,1027],{"class":802},"\u003C!-- LCP hero: eager, high priority, sized; decoding left to the browser (sync is fine for LCP). -->\n",[797,1029,1030,1033,1036,1039,1042,1045,1048,1050],{"class":168,"line":806},[797,1031,1032],{"class":820},"\u003C",[797,1034,47],{"class":1035},"sZBmE",[797,1037,1038],{"class":813}," src",[797,1040,1041],{"class":820},"=",[797,1043,1044],{"class":935},"\"\u002Fimg\u002Fhero-1200.avif\"",[797,1046,1047],{"class":813}," srcset",[797,1049,1041],{"class":820},[797,1051,1052],{"class":935},"\"\u002Fimg\u002Fhero-800.avif 800w, \u002Fimg\u002Fhero-1200.avif 1200w\"\n",[797,1054,1055,1058,1060,1063,1066,1068,1071,1074,1076,1079,1082,1084,1087,1090,1092,1095],{"class":168,"line":849},[797,1056,1057],{"class":813},"     sizes",[797,1059,1041],{"class":820},[797,1061,1062],{"class":935},"\"100vw\"",[797,1064,1065],{"class":813}," width",[797,1067,1041],{"class":820},[797,1069,1070],{"class":935},"\"1200\"",[797,1072,1073],{"class":813}," height",[797,1075,1041],{"class":820},[797,1077,1078],{"class":935},"\"675\"",[797,1080,1081],{"class":813}," fetchpriority",[797,1083,1041],{"class":820},[797,1085,1086],{"class":935},"\"high\"",[797,1088,1089],{"class":813}," alt",[797,1091,1041],{"class":820},[797,1093,1094],{"class":935},"\"Trail runner on a ridge\"",[797,1096,1097],{"class":820},">\n",[797,1099,1100],{"class":168,"line":879},[797,1101,1102],{"class":802},"\u003C!-- trade-off: forcing decoding=\"async\" on the LCP image can delay its paint by\n",[797,1104,1105],{"class":168,"line":918},[797,1106,1107],{"class":802},"     a frame in some browsers; leave the default for the hero. -->\n",[788,1109,1113],{"className":1110,"code":1111,"language":1112,"meta":793,"style":793},"language-css shiki shiki-themes github-light-high-contrast github-dark-high-contrast github-light-high-contrast","\u002F* Placeholder that never shifts: the container carries the aspect ratio and a dominant colour. *\u002F\n.card-media { aspect-ratio: 4 \u002F 3; background: var(--dominant, #e8e4dc); }\n.card-media img { width: 100%; height: 100%; object-fit: cover; }\n\u002F* trade-off: a flat colour is the cheapest placeholder but conveys little;\n   use LQIP only where the preview genuinely improves perceived quality. *\u002F\n","css",[28,1114,1115,1120,1166,1209,1214],{"__ignoreMap":793},[797,1116,1117],{"class":168,"line":799},[797,1118,1119],{"class":802},"\u002F* Placeholder that never shifts: the container carries the aspect ratio and a dominant colour. *\u002F\n",[797,1121,1122,1125,1128,1131,1134,1137,1140,1142,1145,1148,1150,1153,1155,1158,1160,1163],{"class":168,"line":806},[797,1123,1124],{"class":813},".card-media",[797,1126,1127],{"class":820}," { ",[797,1129,1130],{"class":813},"aspect-ratio",[797,1132,1133],{"class":820},": ",[797,1135,1136],{"class":813},"4",[797,1138,1139],{"class":820}," \u002F ",[797,1141,260],{"class":813},[797,1143,1144],{"class":820},"; ",[797,1146,1147],{"class":813},"background",[797,1149,1133],{"class":820},[797,1151,1152],{"class":813},"var",[797,1154,967],{"class":820},[797,1156,1157],{"class":837},"--dominant",[797,1159,347],{"class":820},[797,1161,1162],{"class":813},"#e8e4dc",[797,1164,1165],{"class":820},"); }\n",[797,1167,1168,1170,1173,1175,1177,1179,1182,1185,1187,1190,1192,1194,1196,1198,1201,1203,1206],{"class":168,"line":849},[797,1169,1124],{"class":813},[797,1171,1172],{"class":1035}," img",[797,1174,1127],{"class":820},[797,1176,979],{"class":813},[797,1178,1133],{"class":820},[797,1180,1181],{"class":813},"100",[797,1183,1184],{"class":809},"%",[797,1186,1144],{"class":820},[797,1188,1189],{"class":813},"height",[797,1191,1133],{"class":820},[797,1193,1181],{"class":813},[797,1195,1184],{"class":809},[797,1197,1144],{"class":820},[797,1199,1200],{"class":813},"object-fit",[797,1202,1133],{"class":820},[797,1204,1205],{"class":813},"cover",[797,1207,1208],{"class":820},"; }\n",[797,1210,1211],{"class":168,"line":879},[797,1212,1213],{"class":802},"\u002F* trade-off: a flat colour is the cheapest placeholder but conveys little;\n",[797,1215,1216],{"class":168,"line":918},[797,1217,1218],{"class":802},"   use LQIP only where the preview genuinely improves perceived quality. *\u002F\n",[189,1220,1222],{"id":1221},"worked-example-a-photography-marketplace","Worked Example: A Photography Marketplace",[15,1224,1225,1226,1228],{},"A photography marketplace served 2400px-wide JPEGs to every device and used a JavaScript BlurHash decoder for all thumbnails at startup. On mid-tier phones, the hero's render delay was 210ms (decode of an oversized image on a busy main thread), the BlurHash library and decodes added 140ms of startup scripting, and grid scrolling stuttered as large images decoded. The team generated responsive AVIF\u002FJPEG sizes, kept the hero eager with default decoding, set ",[28,1227,519],{}," on grid images, and replaced runtime BlurHash with build-time dominant colours plus 1KB LQIP previews only for above-the-fold cards. LCP render delay fell to 35ms, LCP p75 improved by 400ms overall, TBT dropped by 150ms, and grid scrolling became smooth.",[189,1230,1232],{"id":1231},"perceived-performance-and-placeholders","Perceived Performance and Placeholders",[15,1234,1235,1236,1240],{},"Placeholders do not change LCP directly — LCP measures when the real image paints — but they change how fast the page ",[1237,1238,1239],"em",{},"feels",", and they prevent layout shift. The best placeholder is the cheapest one that communicates layout and tone: a reserved box with a dominant colour costs nothing and prevents CLS; a tiny blurred preview conveys composition; BlurHash and ThumbHash give smoother gradients from a few dozen bytes but need decoding code. Make sure the placeholder never becomes the LCP element: a large, visible LQIP can be counted as an image candidate, recording a fast but meaningless LCP and then a later one when the real image replaces it. Keep placeholders as CSS backgrounds or low-entropy elements that do not compete as candidates.",[189,1242,1244],{"id":1243},"framework-image-components-and-decoding","Framework Image Components and Decoding",[15,1246,1247,1248,1251,1252,1254,1255,1258,1259,1262,1263,1265,1266,1269,1270,347,1273,1276,1277,1280,1281,1284,1285,1288,1289,1292,1293,1276,1296,1298,1299,1301,1302,1304],{},"Framework image components make many of these decisions for you, and it is worth knowing which. Next.js ",[28,1249,1250],{},"next\u002Fimage"," sets ",[28,1253,519],{}," by default, lazy-loads unless ",[28,1256,1257],{},"priority"," (or ",[28,1260,1261],{},"fetchPriority=\"high\"",") is set, and can render a blur placeholder from a tiny inline image; mark the hero with ",[28,1264,1257],{}," so it is eager and preloaded. Nuxt Image's ",[28,1267,1268],{},"\u003CNuxtImg>"," passes through ",[28,1271,1272],{},"loading",[28,1274,1275],{},"decoding"," and ",[28,1278,1279],{},"fetchpriority",", with ",[28,1282,1283],{},"preload"," for heroes and a ",[28,1286,1287],{},"placeholder"," option that generates tiny previews. Astro's ",[28,1290,1291],{},"\u003CImage>"," generates responsive sizes at build time and supports ",[28,1294,1295],{},"loading=\"eager\"",[28,1297,1279],{}," for the LCP image. In every case, two settings matter most: the hero must be eager and prioritised, and the component must emit real ",[28,1300,979],{},"\u002F",[28,1303,1189],{}," (or an aspect ratio) so placeholders and final images occupy the same box.",[788,1306,1310],{"className":1307,"code":1308,"language":1309,"meta":793,"style":793},"language-jsx shiki shiki-themes github-light-high-contrast github-dark-high-contrast github-light-high-contrast","\u002F\u002F next\u002Fimage: hero eager and prioritised; grid images lazy with async decode.\n\u003CImage src={hero} alt=\"\" priority sizes=\"100vw\" placeholder=\"empty\" \u002F>\n{products.map((p) => \u003CImage key={p.id} src={p.image} alt={p.name} width={400} height={300} sizes=\"(max-width: 600px) 50vw, 25vw\" \u002F>)}\n\u002F\u002F trade-off: placeholder=\"blur\" inlines a base64 preview per image into the\n\u002F\u002F HTML payload; use it for a handful of large images, not every grid item.\n","jsx",[28,1311,1312,1317,1363,1439,1444],{"__ignoreMap":793},[797,1313,1314],{"class":168,"line":799},[797,1315,1316],{"class":802},"\u002F\u002F next\u002Fimage: hero eager and prioritised; grid images lazy with async decode.\n",[797,1318,1319,1321,1324,1326,1329,1332,1335,1337,1339,1342,1345,1348,1350,1352,1355,1357,1360],{"class":168,"line":806},[797,1320,1032],{"class":820},[797,1322,1323],{"class":1035},"Image",[797,1325,1038],{"class":813},[797,1327,1328],{"class":809},"={",[797,1330,1331],{"class":820},"hero",[797,1333,1334],{"class":809},"}",[797,1336,1089],{"class":813},[797,1338,1041],{"class":809},[797,1340,1341],{"class":935},"\"\"",[797,1343,1344],{"class":813}," priority",[797,1346,1347],{"class":813}," sizes",[797,1349,1041],{"class":809},[797,1351,1062],{"class":935},[797,1353,1354],{"class":813}," placeholder",[797,1356,1041],{"class":809},[797,1358,1359],{"class":935},"\"empty\"",[797,1361,1362],{"class":820}," \u002F>\n",[797,1364,1365,1368,1370,1372,1374,1376,1378,1381,1383,1386,1388,1391,1393,1395,1397,1400,1402,1404,1406,1409,1411,1413,1415,1418,1420,1422,1424,1427,1429,1431,1433,1436],{"class":168,"line":849},[797,1366,1367],{"class":820},"{products.",[797,1369,924],{"class":830},[797,1371,834],{"class":820},[797,1373,15],{"class":837},[797,1375,840],{"class":820},[797,1377,843],{"class":809},[797,1379,1380],{"class":820}," \u003C",[797,1382,1323],{"class":1035},[797,1384,1385],{"class":813}," key",[797,1387,1328],{"class":809},[797,1389,1390],{"class":820},"p.id",[797,1392,1334],{"class":809},[797,1394,1038],{"class":813},[797,1396,1328],{"class":809},[797,1398,1399],{"class":820},"p.image",[797,1401,1334],{"class":809},[797,1403,1089],{"class":813},[797,1405,1328],{"class":809},[797,1407,1408],{"class":820},"p.name",[797,1410,1334],{"class":809},[797,1412,1065],{"class":813},[797,1414,1328],{"class":809},[797,1416,1417],{"class":813},"400",[797,1419,1334],{"class":809},[797,1421,1073],{"class":813},[797,1423,1328],{"class":809},[797,1425,1426],{"class":813},"300",[797,1428,1334],{"class":809},[797,1430,1347],{"class":813},[797,1432,1041],{"class":809},[797,1434,1435],{"class":935},"\"(max-width: 600px) 50vw, 25vw\"",[797,1437,1438],{"class":820}," \u002F>)}\n",[797,1440,1441],{"class":168,"line":879},[797,1442,1443],{"class":802},"\u002F\u002F trade-off: placeholder=\"blur\" inlines a base64 preview per image into the\n",[797,1445,1446],{"class":168,"line":918},[797,1447,1448],{"class":802},"\u002F\u002F HTML payload; use it for a handful of large images, not every grid item.\n",[189,1450,1452],{"id":1451},"decode-memory-and-image-heavy-pages","Decode, Memory and Image-Heavy Pages",[15,1454,1455],{},"Pages with hundreds of images — galleries, feeds, product grids with infinite scroll — run into a second decode problem: memory. Every decoded bitmap occupies width × height × 4 bytes; a 1200×900 image is about 4MB decoded, regardless of how small its file was. Browsers discard decoded bitmaps for off-screen images under memory pressure and re-decode them when they scroll back into view, which shows up as stutter on low-memory devices. Serving images at display size is again the main fix (a 400×300 thumbnail decodes to under 0.5MB). Beyond that, virtualise long image grids so off-screen images are removed from the DOM, and avoid keeping full-size images in memory for lightboxes until they are opened. These steps reduce both jank and the chance of the browser tab being killed on low-end phones.",[189,1457,1459],{"id":1458},"a-rollout-order-for-decode-and-placeholder-work","A Rollout Order for Decode and Placeholder Work",[15,1461,1462,1463,1465],{},"Sequence the work by impact and risk. First, right-size images — it reduces bytes, decode time and memory at once, and responsive image tooling makes it mechanical. Second, audit the LCP image on each template: eager, high priority, discoverable in HTML, default decoding. Third, set ",[28,1464,519],{}," on everything else and fix any synchronous decode paths in JavaScript galleries and canvases. Fourth, standardise placeholders: aspect-ratio boxes with dominant colours everywhere, richer previews only where design calls for them. Measure LCP render delay and CLS after each step; most sites capture the majority of the benefit in the first two.",[189,1467,1469],{"id":1468},"common-pitfalls","Common Pitfalls",[197,1471,1472,1478,1487,1493,1499,1505,1511],{},[200,1473,1474,1477],{},[203,1475,1476],{},"Oversized images."," Decode cost scales with pixels; serving 2x more width than needed means 4x the pixels.",[200,1479,1480,1486],{},[203,1481,1482,1485],{},[28,1483,1484],{},"decoding=\"sync\""," or forced decodes on many images."," Blocks rendering for each.",[200,1488,1489,1492],{},[203,1490,1491],{},"Lazy-loading the LCP image."," Adds load delay that no decode optimisation can recover.",[200,1494,1495,1498],{},[203,1496,1497],{},"Base64 LQIP for every image in HTML."," Inflates HTML (often the LCP bottleneck itself).",[200,1500,1501,1504],{},[203,1502,1503],{},"Runtime BlurHash on the main thread for many images."," Adds startup scripting.",[200,1506,1507,1510],{},[203,1508,1509],{},"Placeholders without aspect ratio."," Causes layout shift when the real image arrives.",[200,1512,1513,1516],{},[203,1514,1515],{},"Removing and re-adding image elements on re-render."," Triggers repeated decodes.",[189,1518,1520],{"id":1519},"measuring-in-the-field","Measuring in the Field",[15,1522,1523,1524,1527],{},"Use LCP attribution's ",[28,1525,1526],{},"elementRenderDelay"," for image-LCP pages as the primary decode-related field metric, segmented by device class — decode cost is CPU-bound and shows most on low-end devices. For interaction-related decode jank, LoAF entries with long render phases on image-heavy views and scroll jank metrics (dropped frames during scroll) help. Track the share of images served larger than 1.5x their displayed size with a sampled in-page check; it is the leading indicator for decode cost.",[189,1529,1531],{"id":1530},"faq","FAQ",[1533,1534,1537,1541],"details",{"className":1535},[1536],"faq-item",[1538,1539,1540],"summary",{},"Does decoding=\"async\" make images load faster?",[15,1542,1543],{},"No — it affects when decoding is allowed to happen relative to rendering other content, not download speed. It lets the browser paint other content without waiting for the image's decode.",[1533,1545,1547,1550],{"className":1546},[1536],[1538,1548,1549],{},"Is AVIF slower to decode than JPEG?",[15,1551,1552],{},"Generally yes, per pixel. Its much smaller files usually make it a net win, especially when images are right-sized; for very large images on low-end devices, measure both.",[1533,1554,1556,1559],{"className":1555},[1536],[1538,1557,1558],{},"Should the LCP image have a placeholder?",[15,1560,1561],{},"A reserved box with a background colour, yes. A visible blurred preview can briefly become the LCP candidate; if you use one, keep it as a CSS background on the container.",[1533,1563,1565,1568],{"className":1564},[1536],[1538,1566,1567],{},"Can decoding block the main thread?",[15,1569,1570],{},"Usually decoding happens on other threads, but certain cases (synchronous decode for an immediately painted image, canvas draws, very large images) can block. Long Image Decode events on the main thread in a trace confirm it.",[1533,1572,1574,1577],{"className":1573},[1536],[1538,1575,1576],{},"Are progressive JPEGs good for LCP?",[15,1578,1579],{},"They can improve perceived load by painting early scans; how LCP accounts for progressive paints has varied across browser versions. Do not choose JPEG over AVIF for this alone.",[1533,1581,1583,1586],{"className":1582},[1536],[1538,1584,1585],{},"What is the cheapest effective placeholder?",[15,1587,1588],{},"A container with the right aspect ratio and the image's dominant colour as background. It prevents CLS, costs a few bytes of CSS, and needs no JavaScript.",[1533,1590,1592,1595],{"className":1591},[1536],[1538,1593,1594],{},"Do image CDNs help with decode cost?",[15,1596,1597],{},"Indirectly: they serve the right size and format per request, which is the biggest lever on decode time. Some also generate placeholders (dominant colour, LQIP) on the fly, removing the need for a build step.",[1533,1599,1601,1604],{"className":1600},[1536],[1538,1602,1603],{},"Does lazy loading affect decoding?",[15,1605,1606,1607,1610,1611,1613],{},"Lazy-loaded images are requested and decoded only as they approach the viewport, which spreads decode work across scrolling rather than concentrating it at load. Combine ",[28,1608,1609],{},"loading=\"lazy\""," with ",[28,1612,519],{}," for below-the-fold images so decodes do not block frames during scroll.",[1533,1615,1617,1620],{"className":1616},[1536],[1538,1618,1619],{},"Should placeholders be generated at build time or runtime?",[15,1621,1622],{},"Build time (or by an image CDN) whenever possible: the placeholder is then plain CSS or a tiny image with no JavaScript cost. Runtime generation makes sense only for user-uploaded images you cannot process ahead of time.",[1533,1624,1626,1629],{"className":1625},[1536],[1538,1627,1628],{},"How do I see decode time in DevTools?",[15,1630,1631],{},"Record a Performance trace with CPU throttling and look for \"Image Decode\" events; selecting one shows the image URL and duration, and the track it appears on tells you whether it ran on the main thread or a raster\u002Fdecoder thread.",[189,1633,1635],{"id":1634},"guides-in-this-topic","Guides in This Topic",[197,1637,1638,1644,1650,1655],{},[200,1639,1640,1643],{},[19,1641,1642],{"href":527},"Using decoding=async and the decode() API"," — controlling when decoding happens.",[200,1645,1646,1649],{},[19,1647,1648],{"href":536},"Avoiding main-thread image decode jank"," — keeping decode out of interactions and scroll.",[200,1651,1652,1654],{},[19,1653,549],{"href":548}," — fixing late-discovered heroes.",[200,1656,1657,1659],{},[19,1658,558],{"href":557}," — choosing placeholders by cost and effect.",[189,1661,1663],{"id":1662},"related","Related",[197,1665,1666,1673,1680,1687],{},[200,1667,1668,1672],{},[19,1669,1671],{"href":1670},"\u002Fimage-media-optimization\u002Fresponsive-images-with-srcset-and-sizes\u002F","Responsive images with srcset and sizes"," — right-sizing that reduces decode work.",[200,1674,1675,1679],{},[19,1676,1678],{"href":1677},"\u002Fcore-web-vitals-measurement\u002Fmeasuring-lcp-with-chrome-devtools\u002F","Measuring LCP with Chrome DevTools"," — reading render delay.",[200,1681,1682,1686],{},[19,1683,1685],{"href":1684},"\u002Fimage-media-optimization\u002Flazy-loading-images-without-hurting-lcp\u002F","Lazy loading images without hurting LCP"," — load timing for non-hero images.",[200,1688,1689,1693],{},[19,1690,1692],{"href":1691},"\u002Fcore-web-vitals-measurement\u002Fprofiling-event-handlers-for-inp\u002Freducing-presentation-delay-from-large-dom-updates\u002F","Reducing presentation delay from large DOM updates"," — when images arrive during interactions.",[1695,1696,1698],"script",{"type":1697},"application\u002Fld+json","\n{\n  \"@context\": \"https:\u002F\u002Fschema.org\",\n  \"@type\": \"HowTo\",\n  \"name\": \"Image Decoding & Placeholders: From Bytes to Pixels\",\n  \"description\": \"What happens between an image's bytes arriving and its pixels appearing, and how decoding strategy and placeholders affect LCP, INP and perceived performance.\",\n  \"step\": [\n    {\n      \"@type\": \"HowToStep\",\n      \"position\": 1,\n      \"name\": \"Environment Setup: Make Decode Visible\",\n      \"text\": \"Record a throttled trace (4x CPU) of a hero-image page.\"\n    },\n    {\n      \"@type\": \"HowToStep\",\n      \"position\": 2,\n      \"name\": \"Capture a Baseline\",\n      \"text\": \"From field data, take LCP render delay p75 for image-LCP templates.\"\n    },\n    {\n      \"@type\": \"HowToStep\",\n      \"position\": 3,\n      \"name\": \"Isolate the Bottleneck\",\n      \"text\": \"If render delay is large and the trace shows long decodes, the image is oversized or decoding synchronously.\"\n    },\n    {\n      \"@type\": \"HowToStep\",\n      \"position\": 4,\n      \"name\": \"Apply the Fixes\",\n      \"text\": \"1.\"\n    }\n  ]\n}\n",[1695,1700,1701],{"type":1697},"\n{\n  \"@context\": \"https:\u002F\u002Fschema.org\",\n  \"@type\": \"TechArticle\",\n  \"headline\": \"Image Decoding & Placeholders: From Bytes to Pixels\",\n  \"description\": \"What happens between an image's bytes arriving and its pixels appearing, and how decoding strategy and placeholders affect LCP, INP and perceived performance.\",\n  \"datePublished\": \"2026-10-06\",\n  \"dateModified\": \"2026-10-06\",\n  \"author\": {\n    \"@type\": \"Organization\",\n    \"name\": \"frontend-performance.com\"\n  },\n  \"publisher\": {\n    \"@type\": \"Organization\",\n    \"name\": \"frontend-performance.com\"\n  },\n  \"mainEntityOfPage\": {\n    \"@type\": \"WebPage\",\n    \"@id\": \"https:\u002F\u002Ffrontend-performance.com\u002Fimage-media-optimization\u002Fimage-decoding-and-placeholders\u002F\"\n  }\n}\n",[1695,1703,1704],{"type":1697},"\n{\n  \"@context\": \"https:\u002F\u002Fschema.org\",\n  \"@type\": \"BreadcrumbList\",\n  \"itemListElement\": [\n    {\n      \"@type\": \"ListItem\",\n      \"position\": 1,\n      \"name\": \"Home\",\n      \"item\": \"https:\u002F\u002Ffrontend-performance.com\u002F\"\n    },\n    {\n      \"@type\": \"ListItem\",\n      \"position\": 2,\n      \"name\": \"Image & Media Optimization\",\n      \"item\": \"https:\u002F\u002Ffrontend-performance.com\u002Fimage-media-optimization\u002F\"\n    },\n    {\n      \"@type\": \"ListItem\",\n      \"position\": 3,\n      \"name\": \"Image Decoding & Placeholders\",\n      \"item\": \"https:\u002F\u002Ffrontend-performance.com\u002Fimage-media-optimization\u002Fimage-decoding-and-placeholders\u002F\"\n    }\n  ]\n}\n",[1706,1707,1708],"style",{},"html pre.shiki code .sjfSM, html code.shiki .sjfSM{--shiki-default:#66707B;--shiki-dark:#BDC4CC;--shiki-light:#66707B}html pre.shiki code .sPARh, html code.shiki .sPARh{--shiki-default:#A0111F;--shiki-dark:#FF9492;--shiki-light:#A0111F}html pre.shiki code .sPXB4, html code.shiki .sPXB4{--shiki-default:#023B95;--shiki-dark:#91CBFF;--shiki-light:#023B95}html pre.shiki code .saISM, html code.shiki .saISM{--shiki-default:#0E1116;--shiki-dark:#F0F3F6;--shiki-light:#0E1116}html pre.shiki code .smZ65, html code.shiki .smZ65{--shiki-default:#622CBC;--shiki-dark:#DBB7FF;--shiki-light:#622CBC}html pre.shiki code .sQw3B, html code.shiki .sQw3B{--shiki-default:#702C00;--shiki-dark:#FFB757;--shiki-light:#702C00}html pre.shiki code .sZ8jY, html code.shiki .sZ8jY{--shiki-default:#032563;--shiki-dark:#ADDCFF;--shiki-light:#032563}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 .sZBmE, html code.shiki .sZBmE{--shiki-default:#024C1A;--shiki-dark:#72F088;--shiki-light:#024C1A}",{"title":793,"searchDepth":806,"depth":806,"links":1710},[1711,1712,1713,1714,1715,1716,1717,1718,1719,1720,1721,1722,1723,1724,1725,1726,1727,1728,1729,1730],{"id":191,"depth":806,"text":192},{"id":312,"depth":806,"text":313},{"id":354,"depth":806,"text":355},{"id":361,"depth":806,"text":362},{"id":368,"depth":806,"text":369},{"id":501,"depth":806,"text":502},{"id":561,"depth":806,"text":562},{"id":744,"depth":806,"text":745},{"id":782,"depth":806,"text":783},{"id":1014,"depth":806,"text":1015},{"id":1221,"depth":806,"text":1222},{"id":1231,"depth":806,"text":1232},{"id":1243,"depth":806,"text":1244},{"id":1451,"depth":806,"text":1452},{"id":1458,"depth":806,"text":1459},{"id":1468,"depth":806,"text":1469},{"id":1519,"depth":806,"text":1520},{"id":1530,"depth":806,"text":1531},{"id":1634,"depth":806,"text":1635},{"id":1662,"depth":806,"text":1663},"What happens between an image's bytes arriving and its pixels appearing, and how decoding strategy and placeholders affect LCP, INP and perceived performance.","md",{"slug":1734,"type":1735,"breadcrumb":1736,"datePublished":1742,"dateModified":1742},"image-decoding-and-placeholders","topic",[1737,1739,1740],{"name":1738,"url":1301},"Home",{"name":22,"url":21},{"name":5,"url":1741},"\u002Fimage-media-optimization\u002Fimage-decoding-and-placeholders\u002F","2026-10-06",true,"\u002Fimage-media-optimization\u002Fimage-decoding-and-placeholders",{"title":1746,"description":1747},"Image Decoding & Placeholders for LCP and INP","Understand image decode cost, decoding=async and img.decode(), CSS background images as LCP elements, and LQIP vs BlurHash placeholders that improve perceived speed.","image-media-optimization\u002Fimage-decoding-and-placeholders\u002Findex","sq-pkHdSpd7cTtigQNWzGrHz1rnNgGFIdoh0J1NhrHg",[1751,1755],{"title":1752,"path":1753,"stem":1754},"Using fetchpriority to Prioritize the LCP Image","\u002Fimage-media-optimization\u002Fimage-cdns-and-fetchpriority\u002Fusing-fetchpriority-to-prioritize-the-lcp-image","image-media-optimization\u002Fimage-cdns-and-fetchpriority\u002Fusing-fetchpriority-to-prioritize-the-lcp-image\u002Findex",{"title":1756,"path":1757,"stem":1758},"Avoiding Main-Thread Image Decode Jank","\u002Fimage-media-optimization\u002Fimage-decoding-and-placeholders\u002Favoiding-main-thread-image-decode-jank","image-media-optimization\u002Fimage-decoding-and-placeholders\u002Favoiding-main-thread-image-decode-jank\u002Findex",1791308077013]