[{"data":1,"prerenderedAt":1140},["ShallowReactive",2],{"content:\u002Fimage-media-optimization\u002Fimage-decoding-and-placeholders\u002Fusing-decoding-async-and-the-decode-api":3,"surroundings:\u002Fimage-media-optimization\u002Fimage-decoding-and-placeholders\u002Fusing-decoding-async-and-the-decode-api":1131},{"id":4,"title":5,"body":6,"description":1110,"extension":1111,"meta":1112,"navigation":1124,"path":1125,"seo":1126,"stem":1129,"__hash__":1130},"content\u002Fimage-media-optimization\u002Fimage-decoding-and-placeholders\u002Fusing-decoding-async-and-the-decode-api\u002Findex.md","Using decoding=async and the decode() API",{"type":7,"value":8,"toc":1093},"minimark",[9,14,50,65,209,214,249,253,259,269,275,281,391,395,400,491,495,561,564,568,700,704,710,843,847,850,854,863,867,879,883,921,925,941,949,958,964,968,983,992,1001,1013,1033,1042,1051,1055,1078,1083,1086,1089],[10,11,13],"h1",{"id":12},"how-to-use-decodingasync-and-the-imgdecode-api","How to Use decoding=\"async\" and the img.decode() API",[15,16,17,18,23,24,28,29,33,34,37,38,41,42,45,46,49],"p",{},"This guide covers the two explicit decode controls in ",[19,20,22],"a",{"href":21},"\u002Fimage-media-optimization\u002Fimage-decoding-and-placeholders\u002F","Image Decoding & Placeholders",", within ",[19,25,27],{"href":26},"\u002Fimage-media-optimization\u002F","Image & Media Optimization",". Browsers have to decode an image before painting it, and they normally choose when and where to do it. Two tools let you influence that choice. The ",[30,31,32],"code",{},"decoding"," attribute (",[30,35,36],{},"sync",", ",[30,39,40],{},"async"," or ",[30,43,44],{},"auto",") tells the browser whether it may render other content before this image is decoded. The ",[30,47,48],{},"decode()"," method on an image element returns a promise that resolves when the image is decoded and ready to paint without delay.",[15,51,52,53,56,57,59,60,64],{},"They solve opposite problems. ",[30,54,55],{},"decoding=\"async\""," prevents a large image's decode from holding up the rest of a frame — useful for images that appear during scrolling or alongside other content. ",[30,58,48],{}," lets you wait for decoding ",[61,62,63],"em",{},"before"," putting an image on screen — useful when swapping images in a gallery or carousel, where inserting an undecoded image causes a blank flash or a dropped frame. Neither is a general speed-up, and applying them carelessly to the LCP image can make LCP worse.",[15,66,67],{},[68,69,75,76,75,83,75,87,75,90,75,99,75,105,75,111,75,118,75,122,75,127,75,130,75,134,75,138,75,142,75,144,75,148,75,150,75,153,75,156,75,160,75,164,75,167,75,169,75,172,75,175,75,179,75,184,75,187,75,189,75,192,75,195,75,199,75,201,75,204,75,206,75],"svg",{"viewBox":70,"width":71,"role":72,"ariaLabel":73,"style":74},"0 0 760 228","100%","img","Comparison of the decoding attribute values and the decode() method by behaviour and typical use.","height:auto;max-width:760px;display:block;margin:1.75rem auto;font-family:inherit;color:var(--fp-svg-ink)"," ",[77,78],"rect",{"className":79,"x":81,"y":81,"width":71,"height":71,"fill":82},[80],"svg-canvas","0","#ffffff",[84,85,86],"title",{},"Decode controls compared",[88,89,73],"desc",{},[77,91],{"x":92,"y":92,"width":93,"height":94,"rx":95,"fill":96,"stroke":97,"style":98},"1","758","226","10","none","currentColor","stroke-opacity:0.18",[100,101,86],"text",{"x":102,"y":103,"fill":97,"style":104},"28.0","34.0","font-size:16px;font-weight:700",[77,106],{"x":102,"y":107,"width":108,"height":109,"rx":81,"fill":97,"stroke":97,"style":110},"56.0","155.3","30.0","fill-opacity:0.06;stroke-opacity:0.4",[100,112,117],{"x":113,"y":114,"fill":97,"style":115,"textAnchor":116},"38.0","75.5","font-size:12.5px;font-weight:700","start","Control",[77,119],{"x":120,"y":107,"width":121,"height":109,"rx":81,"fill":97,"stroke":97,"style":110},"183.3","274.3",[100,123,126],{"x":124,"y":114,"fill":97,"style":115,"textAnchor":125},"320.5","middle","Behaviour",[77,128],{"x":129,"y":107,"width":121,"height":109,"rx":81,"fill":97,"stroke":97,"style":110},"457.7",[100,131,133],{"x":132,"y":114,"fill":97,"style":115,"textAnchor":125},"594.8","Use for",[77,135],{"x":102,"y":136,"width":108,"height":109,"rx":81,"fill":96,"stroke":97,"style":137},"86.0","stroke-opacity:0.35",[100,139,141],{"x":113,"y":140,"fill":97,"style":115,"textAnchor":116},"105.5","decoding=auto (default)",[77,143],{"x":120,"y":136,"width":121,"height":109,"rx":81,"fill":97,"stroke":97,"style":110},[100,145,147],{"x":124,"y":140,"fill":97,"style":146,"textAnchor":125},"font-size:12px","browser decides",[77,149],{"x":129,"y":136,"width":121,"height":109,"rx":81,"fill":96,"stroke":97,"style":137},[100,151,152],{"x":132,"y":140,"fill":97,"style":146,"textAnchor":125},"LCP image, most cases",[77,154],{"x":102,"y":155,"width":108,"height":109,"rx":81,"fill":96,"stroke":97,"style":137},"116.0",[100,157,159],{"x":113,"y":158,"fill":97,"style":115,"textAnchor":116},"135.5","decoding=async",[77,161],{"x":120,"y":155,"width":121,"height":109,"rx":81,"fill":162,"stroke":162,"style":163},"#0466c8","fill-opacity:0.14;stroke-opacity:0.9",[100,165,166],{"x":124,"y":158,"fill":97,"style":146,"textAnchor":125},"other content may paint first",[77,168],{"x":129,"y":155,"width":121,"height":109,"rx":81,"fill":96,"stroke":97,"style":137},[100,170,171],{"x":132,"y":158,"fill":97,"style":146,"textAnchor":125},"below-the-fold, large non-LCP images",[77,173],{"x":102,"y":174,"width":108,"height":109,"rx":81,"fill":96,"stroke":97,"style":137},"146.0",[100,176,178],{"x":113,"y":177,"fill":97,"style":115,"textAnchor":116},"165.5","decoding=sync",[77,180],{"x":120,"y":174,"width":121,"height":109,"rx":81,"fill":181,"stroke":182,"style":183},"#ffc300","#b8860b","fill-opacity:0.24;stroke-opacity:0.9",[100,185,186],{"x":124,"y":177,"fill":97,"style":146,"textAnchor":125},"frame waits for decode",[77,188],{"x":129,"y":174,"width":121,"height":109,"rx":81,"fill":96,"stroke":97,"style":137},[100,190,191],{"x":132,"y":177,"fill":97,"style":146,"textAnchor":125},"rarely — avoid flashes in specific UI",[77,193],{"x":102,"y":194,"width":108,"height":109,"rx":81,"fill":96,"stroke":97,"style":137},"176.0",[100,196,198],{"x":113,"y":197,"fill":97,"style":115,"textAnchor":116},"195.5","img.decode() promise",[77,200],{"x":120,"y":194,"width":121,"height":109,"rx":81,"fill":162,"stroke":162,"style":163},[100,202,203],{"x":124,"y":197,"fill":97,"style":146,"textAnchor":125},"await decode before inserting",[77,205],{"x":129,"y":194,"width":121,"height":109,"rx":81,"fill":96,"stroke":97,"style":137},[100,207,208],{"x":132,"y":197,"fill":97,"style":146,"textAnchor":125},"galleries, carousels, swaps",[210,211,213],"h2",{"id":212},"rapid-diagnosis","Rapid Diagnosis",[215,216,217,225,231,240],"ul",{},[218,219,220,224],"li",{},[221,222,223],"strong",{},"Look for Image Decode events"," in a throttled trace during scroll or interactions; long ones on the main thread or within the interaction's frame indicate decode-related jank.",[218,226,227,230],{},[221,228,229],{},"Watch for flashes."," In carousels or galleries, a blank or partially drawn frame when switching images indicates insertion before decoding.",[218,232,233,236,237,239],{},[221,234,235],{},"Check LCP render delay."," If it grew after adding ",[30,238,55],{}," site-wide, the hero is affected.",[218,241,242,245,246,248],{},[221,243,244],{},"Check component defaults."," Some image components set ",[30,247,55],{}," on every image, including the hero.",[210,250,252],{"id":251},"root-cause-analysis","Root Cause Analysis",[15,254,255,258],{},[221,256,257],{},"1. Synchronous decode in the rendering path."," Large images inserted and painted in the same frame can force a synchronous decode, delaying the frame.",[15,260,261,264,265,268],{},[221,262,263],{},"2. Swaps without pre-decoding."," Changing ",[30,266,267],{},"src"," on a visible image shows nothing (or the old image) until the new one decodes.",[15,270,271,274],{},[221,272,273],{},"3. Async decode on the LCP image."," Allowing the browser to paint other content first can push the hero's paint (and LCP) a frame later.",[15,276,277,280],{},[221,278,279],{},"4. Too many simultaneous decodes."," Galleries that decode dozens of large images at once compete for decoder threads and memory.",[15,282,283],{},[68,284,75,287,75,290,75,293,75,295,75,298,75,300,75,304,75,310,75,316,75,320,75,324,75,328,75,332,75,336,75,339,75,344,75,348,75,355,75,361,75,365,75,368,75,372,75,375,75,379,75,383,75,387,75],{"viewBox":285,"width":71,"role":72,"ariaLabel":286,"style":74},"0 0 760 178","Timelines comparing a gallery image swap that shows a blank frame while decoding with one that awaits decode before swapping.",[77,288],{"className":289,"x":81,"y":81,"width":71,"height":71,"fill":82},[80],[84,291,292],{},"Swapping a gallery image with and without decode()",[88,294,286],{},[77,296],{"x":92,"y":92,"width":93,"height":297,"rx":95,"fill":96,"stroke":97,"style":98},"176",[100,299,292],{"x":102,"y":103,"fill":97,"style":104},[100,301,303],{"x":102,"y":302,"fill":97,"style":115},"74.0","src swap",[77,305],{"x":306,"y":107,"width":307,"height":308,"rx":309,"fill":97,"stroke":97,"style":110},"120.2","36.4","26.0","3",[100,311,315],{"x":312,"y":313,"fill":97,"style":314,"textAnchor":125},"138.4","73.5","font-size:11.5px","swap",[77,317],{"x":318,"y":107,"width":319,"height":308,"rx":309,"fill":181,"stroke":182,"style":183},"158.1","225.9",[100,321,323],{"x":322,"y":313,"fill":97,"style":314,"textAnchor":125},"271.1","blank \u002F old frame",[77,325],{"x":326,"y":107,"width":327,"height":308,"rx":309,"fill":162,"stroke":162,"style":163},"385.6","74.3",[100,329,331],{"x":330,"y":313,"fill":97,"style":314,"textAnchor":125},"422.7","paint",[100,333,335],{"x":102,"y":334,"fill":97,"style":115},"112.0","decode() first",[77,337],{"x":306,"y":338,"width":319,"height":308,"rx":309,"fill":97,"stroke":97,"style":110},"94.0",[100,340,343],{"x":341,"y":342,"fill":97,"style":314,"textAnchor":125},"233.2","111.5","decode (old image visible)",[77,345],{"x":346,"y":338,"width":347,"height":308,"rx":309,"fill":162,"stroke":162,"style":163},"347.7","55.4",[349,350],"line",{"x1":351,"y1":352,"x2":353,"y2":352,"stroke":97,"style":354},"119.5","134.0","726.0","stroke-opacity:0.4",[100,356,360],{"x":357,"y":358,"fill":359,"style":314,"textAnchor":125},"130.2","151.0","#51617a","0ms",[100,362,364],{"x":363,"y":358,"fill":359,"style":314,"textAnchor":125},"195.3","20ms",[100,366,367],{"x":322,"y":358,"fill":359,"style":314,"textAnchor":125},"40ms",[100,369,371],{"x":370,"y":358,"fill":359,"style":314,"textAnchor":125},"346.9","60ms",[100,373,374],{"x":330,"y":358,"fill":359,"style":314,"textAnchor":125},"80ms",[100,376,378],{"x":377,"y":358,"fill":359,"style":314,"textAnchor":125},"498.6","100ms",[100,380,382],{"x":381,"y":358,"fill":359,"style":314,"textAnchor":125},"574.4","120ms",[100,384,386],{"x":385,"y":358,"fill":359,"style":314,"textAnchor":125},"650.2","140ms",[100,388,390],{"x":389,"y":358,"fill":359,"style":314,"textAnchor":125},"715.3","160ms",[210,392,394],{"id":393},"step-by-step-resolution","Step-by-Step Resolution",[396,397,399],"h3",{"id":398},"_1-leave-the-lcp-image-on-default-decoding","1. Leave the LCP image on default decoding",[401,402,407],"pre",{"className":403,"code":404,"language":405,"meta":406,"style":406},"language-html shiki shiki-themes github-light-high-contrast github-dark-high-contrast github-light-high-contrast","\u003Cimg src=\"\u002Fimg\u002Fhero-1200.avif\" width=\"1200\" height=\"675\" fetchpriority=\"high\" alt=\"…\">\n\u003C!-- No decoding attribute: the browser treats it as important.\n     trade-off: if the hero is enormous and shares the frame with critical text,\n     decoding=\"async\" might let text paint first — but LCP will usually be the\n     image anyway, so measure before changing it. -->\n","html","",[30,408,409,466,473,479,485],{"__ignoreMap":406},[410,411,413,417,420,424,427,431,434,436,439,442,444,447,450,452,455,458,460,463],"span",{"class":349,"line":412},1,[410,414,416],{"class":415},"saISM","\u003C",[410,418,72],{"class":419},"sZBmE",[410,421,423],{"class":422},"sPXB4"," src",[410,425,426],{"class":415},"=",[410,428,430],{"class":429},"sZ8jY","\"\u002Fimg\u002Fhero-1200.avif\"",[410,432,433],{"class":422}," width",[410,435,426],{"class":415},[410,437,438],{"class":429},"\"1200\"",[410,440,441],{"class":422}," height",[410,443,426],{"class":415},[410,445,446],{"class":429},"\"675\"",[410,448,449],{"class":422}," fetchpriority",[410,451,426],{"class":415},[410,453,454],{"class":429},"\"high\"",[410,456,457],{"class":422}," alt",[410,459,426],{"class":415},[410,461,462],{"class":429},"\"…\"",[410,464,465],{"class":415},">\n",[410,467,469],{"class":349,"line":468},2,[410,470,472],{"class":471},"sjfSM","\u003C!-- No decoding attribute: the browser treats it as important.\n",[410,474,476],{"class":349,"line":475},3,[410,477,478],{"class":471},"     trade-off: if the hero is enormous and shares the frame with critical text,\n",[410,480,482],{"class":349,"line":481},4,[410,483,484],{"class":471},"     decoding=\"async\" might let text paint first — but LCP will usually be the\n",[410,486,488],{"class":349,"line":487},5,[410,489,490],{"class":471},"     image anyway, so measure before changing it. -->\n",[396,492,494],{"id":493},"_2-use-decodingasync-for-non-lcp-images","2. Use decoding=\"async\" for non-LCP images",[401,496,498],{"className":403,"code":497,"language":405,"meta":406,"style":406},"\u003Cimg src=\"\u002Fimg\u002Fcard-400.avif\" width=\"400\" height=\"300\" loading=\"lazy\" decoding=\"async\" alt=\"…\">\n\u003C!-- trade-off: async images may appear a frame after surrounding content,\n     which is invisible for below-the-fold images and fine with a placeholder. -->\n",[30,499,500,551,556],{"__ignoreMap":406},[410,501,502,504,506,508,510,513,515,517,520,522,524,527,530,532,535,538,540,543,545,547,549],{"class":349,"line":412},[410,503,416],{"class":415},[410,505,72],{"class":419},[410,507,423],{"class":422},[410,509,426],{"class":415},[410,511,512],{"class":429},"\"\u002Fimg\u002Fcard-400.avif\"",[410,514,433],{"class":422},[410,516,426],{"class":415},[410,518,519],{"class":429},"\"400\"",[410,521,441],{"class":422},[410,523,426],{"class":415},[410,525,526],{"class":429},"\"300\"",[410,528,529],{"class":422}," loading",[410,531,426],{"class":415},[410,533,534],{"class":429},"\"lazy\"",[410,536,537],{"class":422}," decoding",[410,539,426],{"class":415},[410,541,542],{"class":429},"\"async\"",[410,544,457],{"class":422},[410,546,426],{"class":415},[410,548,462],{"class":429},[410,550,465],{"class":415},[410,552,553],{"class":349,"line":468},[410,554,555],{"class":471},"\u003C!-- trade-off: async images may appear a frame after surrounding content,\n",[410,557,558],{"class":349,"line":475},[410,559,560],{"class":471},"     which is invisible for below-the-fold images and fine with a placeholder. -->\n",[15,562,563],{},"Expected outcome: scrolling through image-heavy content stays smooth because decodes do not hold up frames.",[396,565,567],{"id":566},"_3-pre-decode-before-swapping-images","3. Pre-decode before swapping images",[401,569,573],{"className":570,"code":571,"language":572,"meta":406,"style":406},"language-javascript shiki shiki-themes github-light-high-contrast github-dark-high-contrast github-light-high-contrast","async function showSlide(imgEl, nextSrc) {\n  const next = new Image();\n  next.src = nextSrc;\n  try { await next.decode(); } catch { \u002F* decode failed: fall back to normal swap *\u002F }\n  imgEl.src = nextSrc;                              \u002F\u002F now paints without a blank frame\n}\n\u002F\u002F trade-off: the old slide stays visible until the next one is decoded, so\n\u002F\u002F rapid clicking feels slightly less immediate. Show a subtle loading indicator\n\u002F\u002F on the control for slow networks.\n","javascript",[30,574,575,602,622,632,663,676,682,688,694],{"__ignoreMap":406},[410,576,577,580,583,587,590,594,596,599],{"class":349,"line":412},[410,578,40],{"class":579},"sPARh",[410,581,582],{"class":579}," function",[410,584,586],{"class":585},"smZ65"," showSlide",[410,588,589],{"class":415},"(",[410,591,593],{"class":592},"sQw3B","imgEl",[410,595,37],{"class":415},[410,597,598],{"class":592},"nextSrc",[410,600,601],{"class":415},") {\n",[410,603,604,607,610,613,616,619],{"class":349,"line":468},[410,605,606],{"class":579},"  const",[410,608,609],{"class":422}," next",[410,611,612],{"class":579}," =",[410,614,615],{"class":579}," new",[410,617,618],{"class":585}," Image",[410,620,621],{"class":415},"();\n",[410,623,624,627,629],{"class":349,"line":475},[410,625,626],{"class":415},"  next.src ",[410,628,426],{"class":579},[410,630,631],{"class":415}," nextSrc;\n",[410,633,634,637,640,643,646,649,652,655,657,660],{"class":349,"line":481},[410,635,636],{"class":579},"  try",[410,638,639],{"class":415}," { ",[410,641,642],{"class":579},"await",[410,644,645],{"class":415}," next.",[410,647,648],{"class":585},"decode",[410,650,651],{"class":415},"(); } ",[410,653,654],{"class":579},"catch",[410,656,639],{"class":415},[410,658,659],{"class":471},"\u002F* decode failed: fall back to normal swap *\u002F",[410,661,662],{"class":415}," }\n",[410,664,665,668,670,673],{"class":349,"line":487},[410,666,667],{"class":415},"  imgEl.src ",[410,669,426],{"class":579},[410,671,672],{"class":415}," nextSrc;                              ",[410,674,675],{"class":471},"\u002F\u002F now paints without a blank frame\n",[410,677,679],{"class":349,"line":678},6,[410,680,681],{"class":415},"}\n",[410,683,685],{"class":349,"line":684},7,[410,686,687],{"class":471},"\u002F\u002F trade-off: the old slide stays visible until the next one is decoded, so\n",[410,689,691],{"class":349,"line":690},8,[410,692,693],{"class":471},"\u002F\u002F rapid clicking feels slightly less immediate. Show a subtle loading indicator\n",[410,695,697],{"class":349,"line":696},9,[410,698,699],{"class":471},"\u002F\u002F on the control for slow networks.\n",[396,701,703],{"id":702},"_4-pre-decode-before-revealing-content-on-interaction","4. Pre-decode before revealing content on interaction",[15,705,706,707,709],{},"When a click reveals an image (an accordion, a modal, a tab), start loading and decoding on hover or focus, and await ",[30,708,48],{}," before revealing, so the interaction's frame does not include decoding work.",[15,711,712],{},[68,713,75,716,75,719,75,722,75,724,75,741,75,744,75,746,75,751,75,757,75,760,75,765,75,772,75,778,75,782,75,787,75,790,75,794,75,796,75,799,75,802,75,805,75,808,75,811,75,814,75,818,75,820,75,823,75,826,75,829,75,833,75,836,75,839,75],{"viewBox":714,"width":71,"role":72,"ariaLabel":715,"style":74},"0 0 760 325","Decision sequence for choosing decoding attributes or the decode() method for an image.",[77,717],{"className":718,"x":81,"y":81,"width":71,"height":71,"fill":82},[80],[84,720,721],{},"Which decode control for this image?",[88,723,715],{},[725,726,727],"defs",{},[728,729,736],"marker",{"id":730,"viewBox":731,"refX":732,"refY":733,"markerWidth":734,"markerHeight":734,"orient":735},"fa46ed2f46","0 0 10 10","9","5","7","auto-start-reverse",[737,738],"path",{"d":739,"fill":97,"style":740},"M0 0 L10 5 L0 10 z","fill-opacity:0.7",[77,742],{"x":92,"y":92,"width":93,"height":743,"rx":95,"fill":96,"stroke":97,"style":98},"323",[100,745,721],{"x":102,"y":103,"fill":97,"style":104},[77,747],{"x":102,"y":107,"width":748,"height":749,"rx":750,"fill":97,"stroke":97,"style":110},"320.0","35.0","6",[100,752,756],{"x":753,"y":754,"fill":97,"style":755,"textAnchor":125},"188.0","78.0","font-size:13px;font-weight:700","Is it the LCP image?",[77,758],{"x":759,"y":107,"width":748,"height":749,"rx":750,"fill":162,"stroke":162,"style":163},"412.0",[100,761,764],{"x":762,"y":754,"fill":97,"style":763,"textAnchor":125},"572.0","font-size:12.5px","Default decoding, eager, fetchpriority=high",[349,766],{"x1":767,"y1":313,"x2":768,"y2":313,"stroke":97,"strokeWidth":769,"style":770,"markerEnd":771},"348.0","410.0","1.5","stroke-opacity:0.6","url(#fa46ed2f46)",[100,773,777],{"x":774,"y":775,"fill":162,"style":776,"textAnchor":125},"380.0","66.5","font-size:11.5px;font-weight:700","yes",[349,779],{"x1":753,"y1":780,"x2":753,"y2":781,"stroke":97,"strokeWidth":769,"style":770,"markerEnd":771},"91.0","125.0",[100,783,786],{"x":784,"y":785,"fill":359,"style":776},"196.0","113.0","no",[77,788],{"x":102,"y":789,"width":748,"height":749,"rx":750,"fill":97,"stroke":97,"style":110},"127.0",[100,791,793],{"x":753,"y":792,"fill":97,"style":755,"textAnchor":125},"149.0","Swapped or revealed by script?",[77,795],{"x":759,"y":789,"width":748,"height":749,"rx":750,"fill":162,"stroke":162,"style":163},[100,797,798],{"x":762,"y":792,"fill":97,"style":763,"textAnchor":125},"Await img.decode() before inserting",[349,800],{"x1":767,"y1":801,"x2":768,"y2":801,"stroke":97,"strokeWidth":769,"style":770,"markerEnd":771},"144.5",[100,803,777],{"x":774,"y":804,"fill":162,"style":776,"textAnchor":125},"137.5",[349,806],{"x1":753,"y1":807,"x2":753,"y2":784,"stroke":97,"strokeWidth":769,"style":770,"markerEnd":771},"162.0",[100,809,786],{"x":784,"y":810,"fill":359,"style":776},"184.0",[77,812],{"x":102,"y":813,"width":748,"height":749,"rx":750,"fill":97,"stroke":97,"style":110},"198.0",[100,815,817],{"x":753,"y":816,"fill":97,"style":755,"textAnchor":125},"220.0","Below the fold or in a long grid?",[77,819],{"x":759,"y":813,"width":748,"height":749,"rx":750,"fill":162,"stroke":162,"style":163},[100,821,822],{"x":762,"y":816,"fill":97,"style":763,"textAnchor":125},"decoding=async + loading=lazy",[349,824],{"x1":767,"y1":825,"x2":768,"y2":825,"stroke":97,"strokeWidth":769,"style":770,"markerEnd":771},"215.5",[100,827,777],{"x":774,"y":828,"fill":162,"style":776,"textAnchor":125},"208.5",[349,830],{"x1":753,"y1":831,"x2":753,"y2":832,"stroke":97,"strokeWidth":769,"style":770,"markerEnd":771},"233.0","267.0",[100,834,786],{"x":784,"y":835,"fill":359,"style":776},"255.0",[77,837],{"x":102,"y":838,"width":748,"height":103,"rx":750,"fill":181,"stroke":182,"style":183},"269.0",[100,840,842],{"x":753,"y":841,"fill":97,"style":763,"textAnchor":125},"290.5","Leave the default",[210,844,846],{"id":845},"verification","Verification",[15,848,849],{},"Record a trace of scrolling and of the gallery or reveal interaction. Image Decode events should not sit inside interaction frames, and there should be no blank frames during swaps (check screenshots in the trace). LCP render delay should be unchanged or improved for the hero. For interactions that reveal images, INP's presentation delay should not include decode time.",[210,851,853],{"id":852},"worked-example-a-product-gallery","Worked Example: A Product Gallery",[15,855,856,857,859,860,862],{},"A product page gallery swapped the main image by setting ",[30,858,267],{}," on click. With 2000px AVIF images, each swap showed a blank frame for 60–90ms on mid-tier phones, and the click interaction's presentation delay included the decode, pushing INP to 260ms for gallery clicks. Switching to ",[30,861,48],{},"-before-swap, preloading the next image on thumbnail hover, and serving gallery images at display size (1000px) removed the blank frames and brought gallery INP to 90ms.",[210,864,866],{"id":865},"interaction-with-lazy-loading-and-placeholders","Interaction With Lazy Loading and Placeholders",[15,868,869,871,872,875,876,878],{},[30,870,55],{}," pairs naturally with ",[30,873,874],{},"loading=\"lazy\""," and sized placeholders: the browser requests the image as it nears the viewport, decodes it without holding frames, and paints it into an already-reserved box with a background colour or blurred preview. The placeholder makes any one-frame delay from async decoding invisible. For images revealed by script, ",[30,877,48],{}," replaces the need for a placeholder by keeping the previous state visible until the new image is ready. Use the two techniques in their own contexts and they rarely conflict.",[210,880,882],{"id":881},"common-mistakes","Common Mistakes",[215,884,885,894,903,912],{},[218,886,887,893],{},[221,888,889,890,892],{},"Setting ",[30,891,55],{}," on everything, including the hero."," Can delay LCP slightly; measure.",[218,895,896,902],{},[221,897,898,899,901],{},"Awaiting ",[30,900,48],{}," on the critical path at load."," Delays rendering for no reason; use it for swaps.",[218,904,905,911],{},[221,906,907,908,910],{},"Ignoring ",[30,909,48],{}," rejections."," Decoding can fail (corrupt image, unsupported format); catch and fall back.",[218,913,914,920],{},[221,915,916,917,919],{},"Calling ",[30,918,48],{}," on huge images repeatedly."," Each call may trigger a decode; cache the decoded element or reuse it.",[210,922,924],{"id":923},"edge-cases","Edge Cases",[15,926,927,75,930,933,934,936,937,940],{},[221,928,929],{},"Canvas drawing.",[30,931,932],{},"drawImage"," with an undecoded image forces a synchronous decode on the main thread; await ",[30,935,48],{}," (or use ",[30,938,939],{},"createImageBitmap",") first.",[15,942,943,948],{},[221,944,945,947],{},[30,946,939],{},"."," Decodes off the main thread and returns a bitmap usable in canvas and workers — ideal for image processing and WebGL textures.",[15,950,951,954,955,957],{},[221,952,953],{},"Browser differences."," Browsers interpret ",[30,956,32],{}," as a hint and differ in defaults; treat it as an optimisation, not a guarantee.",[15,959,960,963],{},[221,961,962],{},"Memory."," Pre-decoding many images holds many bitmaps in memory; pre-decode only the next one or two items.",[210,965,967],{"id":966},"faq","FAQ",[969,970,973,977],"details",{"className":971},[972],"faq-item",[974,975,976],"summary",{},"Does decoding=\"async\" lazy-load images?",[15,978,979,980,982],{},"No. It controls when decoding may happen relative to painting, not when the image is requested. Use ",[30,981,874],{}," for deferring requests.",[969,984,986,989],{"className":985},[972],[974,987,988],{},"Is img.decode() supported everywhere?",[15,990,991],{},"Yes in all modern browsers. Wrap it in try\u002Fcatch, since it rejects for images that fail to decode.",[969,993,995,998],{"className":994},[972],[974,996,997],{},"Does decode() help LCP?",[15,999,1000],{},"Not for the initial load. It helps when images are inserted later by script, avoiding blank frames and keeping decode work out of interaction frames.",[969,1002,1004,1007],{"className":1003},[972],[974,1005,1006],{},"Should I ever use decoding=\"sync\"?",[15,1008,1009,1010,1012],{},"Rarely. It can avoid a flash for small images that must appear in the same frame as surrounding content, at the cost of delaying that frame. For most cases, ",[30,1011,48],{}," is the better tool.",[969,1014,1016,1019],{"className":1015},[972],[974,1017,1018],{},"What about background images in CSS?",[15,1020,1021,1022,1025,1026,1028,1029,1032],{},"CSS backgrounds have no decoding attribute. Preload and pre-decode them via an ",[30,1023,1024],{},"Image"," object and ",[30,1027,48],{}," if they are revealed by script, or use ",[30,1030,1031],{},"\u003Cimg>"," for important images.",[969,1034,1036,1039],{"className":1035},[972],[974,1037,1038],{},"How do I know decode is my problem rather than download?",[15,1040,1041],{},"In the trace, compare when the image's network request finished with when it painted. A large gap filled by Image Decode events (or a busy main thread) points at decoding; a late network finish points at loading.",[969,1043,1045,1048],{"className":1044},[972],[974,1046,1047],{},"Does decode() work for SVG images?",[15,1049,1050],{},"Yes, it resolves when the SVG is ready to render; SVG decoding is usually cheap unless the file is complex, in which case rasterisation cost dominates rather than decoding.",[210,1052,1054],{"id":1053},"related","Related",[215,1056,1057,1064,1071],{},[218,1058,1059,1063],{},[19,1060,1062],{"href":1061},"\u002Fimage-media-optimization\u002Fimage-decoding-and-placeholders\u002Favoiding-main-thread-image-decode-jank\u002F","Avoiding main-thread image decode jank"," — broader decode-jank patterns.",[218,1065,1066,1070],{},[19,1067,1069],{"href":1068},"\u002Fimage-media-optimization\u002Flazy-loading-images-without-hurting-lcp\u002Flazy-loading-images-in-carousels\u002F","Lazy-loading images in carousels"," — combining lazy loading with pre-decoding.",[218,1072,1073,1077],{},[19,1074,1076],{"href":1075},"\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 reveals are slow for other reasons.",[1079,1080,1082],"script",{"type":1081},"application\u002Fld+json","\n{\n  \"@context\": \"https:\u002F\u002Fschema.org\",\n  \"@type\": \"HowTo\",\n  \"name\": \"How to Use decoding=\\\"async\\\" and the img.decode() API\",\n  \"description\": \"How the decoding attribute and the HTMLImageElement.decode() promise control when image decoding happens, with patterns for galleries, carousels and the LCP image.\",\n  \"step\": [\n    {\n      \"@type\": \"HowToStep\",\n      \"position\": 1,\n      \"name\": \"Leave the LCP image on default decoding\",\n      \"text\": \"Leave the LCP image on default decoding\"\n    },\n    {\n      \"@type\": \"HowToStep\",\n      \"position\": 2,\n      \"name\": \"Use decoding=\\\"async\\\" for non-LCP images\",\n      \"text\": \"Expected outcome: scrolling through image-heavy content stays smooth because decodes do not hold up frames.\"\n    },\n    {\n      \"@type\": \"HowToStep\",\n      \"position\": 3,\n      \"name\": \"Pre-decode before swapping images\",\n      \"text\": \"Pre-decode before swapping images\"\n    },\n    {\n      \"@type\": \"HowToStep\",\n      \"position\": 4,\n      \"name\": \"Pre-decode before revealing content on interaction\",\n      \"text\": \"When a click reveals an image (an accordion, a modal, a tab), start loading and decoding on hover or focus, and await decode() before revealing, so the interaction's frame does not include decoding work.\"\n    }\n  ]\n}\n",[1079,1084,1085],{"type":1081},"\n{\n  \"@context\": \"https:\u002F\u002Fschema.org\",\n  \"@type\": \"TechArticle\",\n  \"headline\": \"How to Use decoding=\\\"async\\\" and the img.decode() API\",\n  \"description\": \"How the decoding attribute and the HTMLImageElement.decode() promise control when image decoding happens, with patterns for galleries, carousels and the LCP image.\",\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\u002Fusing-decoding-async-and-the-decode-api\u002F\"\n  }\n}\n",[1079,1087,1088],{"type":1081},"\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      \"@type\": \"ListItem\",\n      \"position\": 4,\n      \"name\": \"Using decoding=async and the decode() API\",\n      \"item\": \"https:\u002F\u002Ffrontend-performance.com\u002Fimage-media-optimization\u002Fimage-decoding-and-placeholders\u002Fusing-decoding-async-and-the-decode-api\u002F\"\n    }\n  ]\n}\n",[1090,1091,1092],"style",{},"html pre.shiki code .saISM, html code.shiki .saISM{--shiki-default:#0E1116;--shiki-dark:#F0F3F6;--shiki-light:#0E1116}html pre.shiki code .sZBmE, html code.shiki .sZBmE{--shiki-default:#024C1A;--shiki-dark:#72F088;--shiki-light:#024C1A}html pre.shiki code .sPXB4, html code.shiki .sPXB4{--shiki-default:#023B95;--shiki-dark:#91CBFF;--shiki-light:#023B95}html pre.shiki code .sZ8jY, html code.shiki .sZ8jY{--shiki-default:#032563;--shiki-dark:#ADDCFF;--shiki-light:#032563}html pre.shiki code .sjfSM, html code.shiki .sjfSM{--shiki-default:#66707B;--shiki-dark:#BDC4CC;--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 .sPARh, html code.shiki .sPARh{--shiki-default:#A0111F;--shiki-dark:#FF9492;--shiki-light:#A0111F}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}",{"title":406,"searchDepth":468,"depth":468,"links":1094},[1095,1096,1097,1103,1104,1105,1106,1107,1108,1109],{"id":212,"depth":468,"text":213},{"id":251,"depth":468,"text":252},{"id":393,"depth":468,"text":394,"children":1098},[1099,1100,1101,1102],{"id":398,"depth":475,"text":399},{"id":493,"depth":475,"text":494},{"id":566,"depth":475,"text":567},{"id":702,"depth":475,"text":703},{"id":845,"depth":468,"text":846},{"id":852,"depth":468,"text":853},{"id":865,"depth":468,"text":866},{"id":881,"depth":468,"text":882},{"id":923,"depth":468,"text":924},{"id":966,"depth":468,"text":967},{"id":1053,"depth":468,"text":1054},"How the decoding attribute and the HTMLImageElement.decode() promise control when image decoding happens, with patterns for galleries, carousels and the LCP image.","md",{"slug":1113,"type":1114,"breadcrumb":1115,"datePublished":1123,"dateModified":1123},"using-decoding-async-and-the-decode-api","article",[1116,1119,1120,1121],{"name":1117,"url":1118},"Home","\u002F",{"name":27,"url":26},{"name":22,"url":21},{"name":5,"url":1122},"\u002Fimage-media-optimization\u002Fimage-decoding-and-placeholders\u002Fusing-decoding-async-and-the-decode-api\u002F","2026-10-06",true,"\u002Fimage-media-optimization\u002Fimage-decoding-and-placeholders\u002Fusing-decoding-async-and-the-decode-api",{"title":1127,"description":1128},"Using decoding=async and img.decode() Correctly","decoding=\"async\" lets the browser paint other content while images decode; img.decode() waits for decoding before insertion. When each helps and when they hurt LCP.","image-media-optimization\u002Fimage-decoding-and-placeholders\u002Fusing-decoding-async-and-the-decode-api\u002Findex","XaW-8LXaqJrNnPcpZqa1NNX04GjHYrxoT3l56Yymc2c",[1132,1136],{"title":1133,"path":1134,"stem":1135},"LQIP vs BlurHash Placeholders","\u002Fimage-media-optimization\u002Fimage-decoding-and-placeholders\u002Flqip-vs-blurhash-placeholders","image-media-optimization\u002Fimage-decoding-and-placeholders\u002Flqip-vs-blurhash-placeholders\u002Findex",{"title":1137,"path":1138,"stem":1139},"Lazy Loading Images Without Hurting LCP","\u002Fimage-media-optimization\u002Flazy-loading-images-without-hurting-lcp","image-media-optimization\u002Flazy-loading-images-without-hurting-lcp\u002Findex",1791308077247]