[{"data":1,"prerenderedAt":1427},["ShallowReactive",2],{"content:\u002Fimage-media-optimization\u002Fresponsive-images-with-srcset-and-sizes\u002F":3,"surroundings:\u002Fimage-media-optimization\u002Fresponsive-images-with-srcset-and-sizes\u002F":1418},{"id":4,"title":5,"body":6,"description":1401,"extension":1402,"meta":1403,"navigation":275,"path":1413,"seo":1414,"stem":1416,"__hash__":1417},"content\u002Fimage-media-optimization\u002Fresponsive-images-with-srcset-and-sizes\u002Findex.md","Responsive Images with srcset and sizes",{"type":7,"value":8,"toc":1390},"minimark",[9,14,46,49,193,198,201,220,232,439,443,454,460,476,480,501,530,613,617,637,663,786,790,843,863,1024,1028,1072,1089,1117,1121,1159,1178,1182,1188,1204,1323,1337,1341,1375,1380,1383,1386],[10,11,13],"h1",{"id":12},"responsive-images-with-srcset-and-sizes-a-diagnostic-workflow-for-right-sized-delivery","Responsive Images with srcset and sizes: A Diagnostic Workflow for Right-Sized Delivery",[15,16,17,18,23,24,28,29,33,34,37,38,41,42,45],"p",{},"This guide extends the ",[19,20,22],"a",{"href":21},"\u002Fimage-media-optimization\u002F","Image & Media Optimization"," discipline into the single highest-leverage technique for cutting transfer weight: serving each viewport the smallest image that still looks sharp. A fixed ",[25,26,27],"code",{},"\u003Cimg src>"," ships one file to every client, which means a 1600px hero gets pushed to a 360px phone that can only ever display a fraction of those pixels. On a mid-tier mobile connection (~1.6 Mbps), every 100KB of wasted image payload adds roughly 0.5s of transfer time, and because the hero image is almost always the ",[19,30,32],{"href":31},"\u002Fcore-web-vitals-measurement\u002Fmeasuring-lcp-with-chrome-devtools\u002F","Largest Contentful Paint"," candidate, that waste lands directly on your LCP budget of 2.5s. Responsive image markup — ",[25,35,36],{},"srcset",", ",[25,39,40],{},"sizes",", and ",[25,43,44],{},"\u003Cpicture>"," — lets the browser select the right candidate before it commits a single byte to the network.",[15,47,48],{},"The goal of this workflow is mechanical precision: declare a set of candidate files, describe the layout slot they will occupy, and let the browser's selection algorithm pick the smallest file that satisfies the device's pixel density. Get the inputs right and a phone downloads a 40KB image where a desktop downloads 180KB — same markup, same component.",[15,50,51],{},[52,53,60,61,60,65,60,69,60,79,60,86,60,94,60,100,60,105,60,109,60,112,60,116,60,119,60,122,60,128,60,132,60,135,60,138,60,144,60,147,60,152,60,157,60,164,60,170,60,174,60,178,60,181,60,185,60,189,60],"svg",{"xmlns":54,"viewBox":55,"width":56,"role":57,"ariaLabel":58,"style":59},"http:\u002F\u002Fwww.w3.org\u002F2000\u002Fsvg","0 0 760 360","100%","img","How the browser selects a srcset candidate from layout width, device pixel ratio, and the sizes attribute","height:auto;max-width:760px;display:block;margin:1.75rem auto;font-family:inherit;color:#001d3d"," ",[62,63,64],"title",{},"srcset candidate selection",[66,67,68],"desc",{},"The browser multiplies the resolved sizes slot width by devicePixelRatio to get a required pixel width, then picks the smallest w-descriptor candidate that meets or exceeds it.",[70,71],"rect",{"x":72,"y":72,"width":73,"height":74,"rx":75,"fill":76,"stroke":77,"style":78},"1","758","358","10","none","currentColor","stroke-opacity:0.18",[80,81,85],"text",{"x":82,"y":83,"fill":77,"style":84},"24","38","font-size:18px;font-weight:700","How the browser picks a srcset candidate",[70,87],{"x":82,"y":88,"width":89,"height":90,"rx":91,"fill":92,"stroke":92,"style":93},"62","220","74","6","#0466c8","fill-opacity:0.14",[80,95,99],{"x":96,"y":97,"fill":77,"style":98},"134","90","font-size:13px;font-weight:600;text-anchor:middle","1. Resolve sizes",[80,101,104],{"x":96,"y":102,"fill":77,"style":103},"110","font-size:12px;text-anchor:middle","slot width = 360 CSS px",[80,106,108],{"x":96,"y":107,"fill":77,"style":103},"127","(first matching media)",[70,110],{"x":111,"y":88,"width":89,"height":90,"rx":91,"fill":92,"stroke":92,"style":93},"270",[80,113,115],{"x":114,"y":97,"fill":77,"style":98},"380","2. Apply DPR",[80,117,118],{"x":114,"y":102,"fill":77,"style":103},"360 x 2 (retina)",[80,120,121],{"x":114,"y":107,"fill":77,"style":103},"= 720 device px needed",[70,123],{"x":124,"y":88,"width":89,"height":90,"rx":91,"fill":125,"stroke":126,"style":127},"516","#ffc300","#b8860b","fill-opacity:0.22",[80,129,131],{"x":130,"y":97,"fill":77,"style":98},"626","3. Pick candidate",[80,133,134],{"x":130,"y":102,"fill":77,"style":103},"smallest w >= 720",[80,136,137],{"x":130,"y":107,"fill":77,"style":103},"chosen: 800w",[139,140],"line",{"x1":141,"y1":142,"x2":111,"y2":142,"stroke":77,"style":143},"244","99","stroke-opacity:0.5",[139,145],{"x1":146,"y1":142,"x2":124,"y2":142,"stroke":77,"style":143},"490",[139,148],{"x1":82,"y1":149,"x2":150,"y2":149,"stroke":77,"style":151},"168","736","stroke-opacity:0.3",[80,153,156],{"x":82,"y":154,"fill":77,"style":155},"198","font-size:14px;font-weight:600","srcset=\"img-400.jpg 400w, img-800.jpg 800w, img-1600.jpg 1600w\"",[70,158],{"x":82,"y":159,"width":160,"height":161,"rx":162,"fill":92,"stroke":92,"style":163},"216","120","40","5","stroke-opacity:0.5;fill-opacity:0.10",[80,165,169],{"x":166,"y":167,"fill":77,"style":168},"84","241","font-size:13px;text-anchor:middle","400w",[70,171],{"x":172,"y":159,"width":160,"height":161,"rx":162,"fill":125,"stroke":126,"style":173},"160","fill-opacity:0.28",[80,175,177],{"x":89,"y":167,"fill":77,"style":176},"font-size:13px;font-weight:700;text-anchor:middle","800w (used)",[70,179],{"x":180,"y":159,"width":160,"height":161,"rx":162,"fill":92,"stroke":92,"style":163},"296",[80,182,184],{"x":183,"y":167,"fill":77,"style":168},"356","1600w",[80,186,188],{"x":82,"y":180,"fill":77,"style":187},"font-size:13px","A wrong sizes value poisons every candidate decision — the byte cost is silent.",[80,190,192],{"x":82,"y":191,"fill":77,"style":187},"326","Browsers may pick a larger file under good network conditions; never a smaller one than required.",[194,195,197],"h2",{"id":196},"_1-environment-setup-and-source-asset-generation","1. Environment Setup and Source Asset Generation",[15,199,200],{},"Responsive delivery is only as good as the candidate set behind it, so the first step is generating a ladder of derivatives from each master asset. A practical ladder spans the full range of real layout widths multiplied by plausible device pixel ratios: for a content image that renders at most 800 CSS pixels wide, you want candidates up to 1600px to satisfy a 2x display. Generate widths at roughly 1.5x steps — 400, 600, 800, 1200, 1600 — which keeps adjacent candidates close enough that the browser rarely over-fetches by more than a few percent.",[15,202,203,204,207,208,212,213,215,216,219],{},"Use a build-time tool such as ",[25,205,206],{},"sharp"," (Node) or your image CDN's transformation API rather than hand-exporting in a design tool. Hand exports drift out of sync and you lose the ability to regenerate the whole ladder when you add a new breakpoint. Pair this with modern formats from the start; the same candidate ladder should exist in AVIF and WebP as covered in ",[19,209,211],{"href":210},"\u002Fimage-media-optimization\u002Fserving-avif-and-webp-with-fallbacks\u002F","serving AVIF and WebP with fallbacks",", because format choice and resolution selection are orthogonal — ",[25,214,36],{}," chooses the size, the ",[25,217,218],{},"type"," negotiation chooses the codec.",[15,221,222,223,226,227,231],{},"The width of the topmost candidate is the one decision that bounds quality: the master asset must be at least as wide as your largest CSS slot multiplied by the highest DPR you intend to serve sharply. For a 1100px content column targeting DPR 2, that is 2200px of intrinsic width, so a 1600w ceiling will still look soft on a retina laptop. Resist the temptation to fix that with ",[25,224,225],{},"image-rendering"," or a sharpening filter — neither adds detail that was never encoded. The only cure for a pixel deficit is more source pixels, which is why the ladder, not the markup, is where high-DPI sharpness is won or lost. Conversely, do not let the ladder run wider than the master; resizing ",[228,229,230],"em",{},"up"," from the master produces interpolated mush that ships real bytes for fake detail, so always assert that the master exceeds the largest width before generating.",[233,234,239],"pre",{"className":235,"code":236,"language":237,"meta":238,"style":238},"language-javascript shiki shiki-themes github-light-high-contrast github-light-high-contrast github-light-high-contrast","\u002F\u002F build-images.js — generate a width ladder with sharp\nimport sharp from 'sharp';\n\nconst widths = [400, 600, 800, 1200, 1600];\nfor (const w of widths) {\n  await sharp('hero-master.jpg')\n    .resize({ width: w })          \u002F\u002F never upscale: master must exceed the largest width\n    .jpeg({ quality: 72 })          \u002F\u002F 72 is a good byte\u002Fquality knee for photos\n    .toFile(`hero-${w}.jpg`);\n}\n\u002F\u002F trade-off: a 5-width ladder doubles storage and build time vs a single file.\n\u002F\u002F For tiny UI icons or images that never resize, skip srcset entirely — the\n\u002F\u002F markup overhead and extra cache entries cost more than they save.\n","javascript","",[25,240,241,249,270,277,319,339,358,373,393,415,421,427,433],{"__ignoreMap":238},[242,243,245],"span",{"class":139,"line":244},1,[242,246,248],{"class":247},"sIIH1","\u002F\u002F build-images.js — generate a width ladder with sharp\n",[242,250,252,256,260,263,267],{"class":139,"line":251},2,[242,253,255],{"class":254},"sP5qI","import",[242,257,259],{"class":258},"syybb"," sharp ",[242,261,262],{"class":254},"from",[242,264,266],{"class":265},"s-_DF"," 'sharp'",[242,268,269],{"class":258},";\n",[242,271,273],{"class":139,"line":272},3,[242,274,276],{"emptyLinePlaceholder":275},true,"\n",[242,278,280,283,287,290,293,296,298,301,303,306,308,311,313,316],{"class":139,"line":279},4,[242,281,282],{"class":254},"const",[242,284,286],{"class":285},"sf6mN"," widths",[242,288,289],{"class":254}," =",[242,291,292],{"class":258}," [",[242,294,295],{"class":285},"400",[242,297,37],{"class":258},[242,299,300],{"class":285},"600",[242,302,37],{"class":258},[242,304,305],{"class":285},"800",[242,307,37],{"class":258},[242,309,310],{"class":285},"1200",[242,312,37],{"class":258},[242,314,315],{"class":285},"1600",[242,317,318],{"class":258},"];\n",[242,320,322,325,328,330,333,336],{"class":139,"line":321},5,[242,323,324],{"class":254},"for",[242,326,327],{"class":258}," (",[242,329,282],{"class":254},[242,331,332],{"class":285}," w",[242,334,335],{"class":254}," of",[242,337,338],{"class":258}," widths) {\n",[242,340,342,345,349,352,355],{"class":139,"line":341},6,[242,343,344],{"class":254},"  await",[242,346,348],{"class":347},"ssM3C"," sharp",[242,350,351],{"class":258},"(",[242,353,354],{"class":265},"'hero-master.jpg'",[242,356,357],{"class":258},")\n",[242,359,361,364,367,370],{"class":139,"line":360},7,[242,362,363],{"class":258},"    .",[242,365,366],{"class":347},"resize",[242,368,369],{"class":258},"({ width: w })          ",[242,371,372],{"class":247},"\u002F\u002F never upscale: master must exceed the largest width\n",[242,374,376,378,381,384,387,390],{"class":139,"line":375},8,[242,377,363],{"class":258},[242,379,380],{"class":347},"jpeg",[242,382,383],{"class":258},"({ quality: ",[242,385,386],{"class":285},"72",[242,388,389],{"class":258}," })          ",[242,391,392],{"class":247},"\u002F\u002F 72 is a good byte\u002Fquality knee for photos\n",[242,394,396,398,401,403,406,409,412],{"class":139,"line":395},9,[242,397,363],{"class":258},[242,399,400],{"class":347},"toFile",[242,402,351],{"class":258},[242,404,405],{"class":265},"`hero-${",[242,407,408],{"class":258},"w",[242,410,411],{"class":265},"}.jpg`",[242,413,414],{"class":258},");\n",[242,416,418],{"class":139,"line":417},10,[242,419,420],{"class":258},"}\n",[242,422,424],{"class":139,"line":423},11,[242,425,426],{"class":247},"\u002F\u002F trade-off: a 5-width ladder doubles storage and build time vs a single file.\n",[242,428,430],{"class":139,"line":429},12,[242,431,432],{"class":247},"\u002F\u002F For tiny UI icons or images that never resize, skip srcset entirely — the\n",[242,434,436],{"class":139,"line":435},13,[242,437,438],{"class":247},"\u002F\u002F markup overhead and extra cache entries cost more than they save.\n",[194,440,442],{"id":441},"_2-capture-a-byte-baseline-across-viewports","2. Capture a Byte Baseline Across Viewports",[15,444,445,446,449,450,453],{},"Before adding markup, quantify the waste you are paying for. Open Chrome DevTools, throttle the Network tab to ",[25,447,448],{},"Fast 3G",", and load the page at three widths: 360px (phone), 768px (tablet), and 1440px (desktop). For each, note the transferred size of the image in the Network panel and the resolved render size in the Elements panel (",[25,451,452],{},"Rendered size"," under the Computed tab). The gap between intrinsic file dimensions and rendered dimensions is your over-download. A 1600px file rendered into a 360px slot at DPR 2 needs only 720 device pixels — you are shipping roughly 4x the necessary pixel area, which scales to far more than 4x the bytes once compression non-linearity is factored in.",[15,455,456,457,459],{},"Record these numbers as your baseline the same way you would capture an LCP baseline. The actionable target is straightforward: the transferred image width should land within one ladder step above the required device-pixel width, never the top of the ladder on a small screen. If your phone load is pulling the 1600w file, the markup is either missing or the ",[25,458,40],{}," attribute is wrong — both are fixed in the next steps.",[15,461,462,463,467,468,471,472,475],{},"Capture two derived numbers alongside the raw bytes, because they convert the abstract waste into a budget you can defend in review. First, the ",[464,465,466],"strong",{},"over-fetch ratio",": transferred pixel area divided by required pixel area (",[25,469,470],{},"rendered width × DPR",")². A ratio near 1.0 is ideal; anything above 2.0 means you are paying double for pixels the screen cannot resolve. Second, the ",[464,473,474],{},"incremental transfer time"," at your target connection: at ~1.6 Mbps an extra 100KB costs roughly 0.5s, and on the LCP image that time is added directly to the loading metric. Tabulating these three columns — bytes, over-fetch ratio, added seconds — across the three viewports gives you a before\u002Fafter scorecard that makes the impact of the fix concrete rather than anecdotal, and it surfaces the worst offender so you optimize the image that actually moves the metric first.",[194,477,479],{"id":478},"_3-isolate-the-bottleneck-density-vs-width-descriptors","3. Isolate the Bottleneck: Density vs Width Descriptors",[15,481,482,484,485,492,493,496,497,500],{},[25,483,36],{}," accepts two mutually exclusive descriptor syntaxes, and choosing the wrong one is the most common structural mistake. ",[464,486,487,488,491],{},"Density descriptors (",[25,489,490],{},"x",")"," describe candidates for a fixed-size image: ",[25,494,495],{},"logo.png 1x, logo@2x.png 2x"," tells the browser \"use the 2x file on a 2x display.\" This is correct only when the image renders at one constant CSS size regardless of viewport — logos, avatars, fixed-width UI chrome. The browser picks purely on ",[25,498,499],{},"devicePixelRatio","; the layout width is irrelevant.",[15,502,503,508,509,511,512,514,515,517,518,520,521,523,524,526,527,529],{},[464,504,505,506,491],{},"Width descriptors (",[25,507,408],{}," describe the intrinsic pixel width of each candidate file and are the right choice for any image whose rendered size changes with the viewport. With ",[25,510,408],{},", the browser cannot guess the layout slot from the markup alone — a flexible image could be full-bleed or sit in a sidebar — so you must supply a ",[25,513,40],{}," attribute telling it how wide the slot will be. The browser combines the resolved ",[25,516,40],{}," width with ",[25,519,499],{}," to compute the required device-pixel width, then selects the smallest candidate that meets or exceeds it. Mixing the two syntaxes in one ",[25,522,36],{}," is invalid; pick ",[25,525,490],{}," for fixed-size images and ",[25,528,408],{}," for fluid ones.",[233,531,535],{"className":532,"code":533,"language":534,"meta":238,"style":238},"language-html shiki shiki-themes github-light-high-contrast github-light-high-contrast github-light-high-contrast","\u003C!-- Density (x): correct ONLY for a fixed-render-size image like a logo -->\n\u003Cimg src=\"logo-160.png\"\n     srcset=\"logo-160.png 1x, logo-320.png 2x\"\n     width=\"160\" height=\"48\" alt=\"Acme\">\n\u003C!-- trade-off: x-descriptors ignore viewport width entirely. Use them on a\n     responsive content image and a phone on a 3x screen will download your\n     largest file even inside a 320px slot. -->\n","html",[25,536,537,542,559,569,598,603,608],{"__ignoreMap":238},[242,538,539],{"class":139,"line":244},[242,540,541],{"class":247},"\u003C!-- Density (x): correct ONLY for a fixed-render-size image like a logo -->\n",[242,543,544,547,550,553,556],{"class":139,"line":251},[242,545,546],{"class":258},"\u003C",[242,548,57],{"class":549},"s-fAs",[242,551,552],{"class":285}," src",[242,554,555],{"class":258},"=",[242,557,558],{"class":265},"\"logo-160.png\"\n",[242,560,561,564,566],{"class":139,"line":272},[242,562,563],{"class":285},"     srcset",[242,565,555],{"class":258},[242,567,568],{"class":265},"\"logo-160.png 1x, logo-320.png 2x\"\n",[242,570,571,574,576,579,582,584,587,590,592,595],{"class":139,"line":279},[242,572,573],{"class":285},"     width",[242,575,555],{"class":258},[242,577,578],{"class":265},"\"160\"",[242,580,581],{"class":285}," height",[242,583,555],{"class":258},[242,585,586],{"class":265},"\"48\"",[242,588,589],{"class":285}," alt",[242,591,555],{"class":258},[242,593,594],{"class":265},"\"Acme\"",[242,596,597],{"class":258},">\n",[242,599,600],{"class":139,"line":321},[242,601,602],{"class":247},"\u003C!-- trade-off: x-descriptors ignore viewport width entirely. Use them on a\n",[242,604,605],{"class":139,"line":341},[242,606,607],{"class":247},"     responsive content image and a phone on a 3x screen will download your\n",[242,609,610],{"class":139,"line":360},[242,611,612],{"class":247},"     largest file even inside a 320px slot. -->\n",[194,614,616],{"id":615},"_4-apply-the-fix-srcset-sizes-for-fluid-images","4. Apply the Fix: srcset + sizes for Fluid Images",[15,618,619,620,622,623,625,626,628,629,632,633,636],{},"For content images, the corrected markup pairs a ",[25,621,408],{}," ladder with a ",[25,624,40],{}," attribute that mirrors your CSS layout. The ",[25,627,40],{}," attribute is a list of ",[25,630,631],{},"(media-condition) length"," pairs evaluated left to right; the first matching condition wins, and a bare final length is the fallback. Crucially, the lengths must reflect the actual rendered width at each breakpoint — including any container max-width, gutters, and grid columns — not the viewport width. A common failure is writing ",[25,634,635],{},"sizes=\"100vw\""," for an image that actually sits in a 720px content column on desktop, which forces the browser to fetch a 1440px-class file for a slot half that size.",[15,638,639,640,643,644,647,648,651,652,654,655,659,660,662],{},"Always set explicit ",[25,641,642],{},"width"," and ",[25,645,646],{},"height"," attributes (or an ",[25,649,650],{},"aspect-ratio"," in CSS) alongside ",[25,653,36],{}," to reserve layout space and avoid the ",[19,656,658],{"href":657},"\u002Fcore-web-vitals-measurement\u002Freducing-cumulative-layout-shift-cls\u002F","Cumulative Layout Shift"," that comes from images snapping in after load. The intrinsic attributes establish the aspect ratio; ",[25,661,36],{}," swaps the actual pixels behind it.",[233,664,666],{"className":532,"code":665,"language":534,"meta":238,"style":238},"\u003C!-- Width (w) + sizes: the right pattern for a fluid content image -->\n\u003Cimg\n  src=\"hero-800.jpg\"\n  srcset=\"hero-400.jpg 400w, hero-600.jpg 600w, hero-800.jpg 800w,\n          hero-1200.jpg 1200w, hero-1600.jpg 1600w\"\n  sizes=\"(max-width: 600px) 100vw,\n         (max-width: 1024px) 50vw,\n         720px\"\n  width=\"1200\" height=\"675\"\n  alt=\"Quarterly revenue dashboard\"\n  decoding=\"async\">\n\u003C!-- trade-off: sizes is a PROMISE about layout. If CSS later changes the slot\n     width and sizes is not updated, the browser keeps choosing against stale\n     assumptions and silently over- or under-fetches. Treat sizes as coupled to\n     your CSS and lint it when breakpoints change. -->\n",[25,667,668,673,680,690,700,705,715,720,725,742,752,764,769,774,780],{"__ignoreMap":238},[242,669,670],{"class":139,"line":244},[242,671,672],{"class":247},"\u003C!-- Width (w) + sizes: the right pattern for a fluid content image -->\n",[242,674,675,677],{"class":139,"line":251},[242,676,546],{"class":258},[242,678,679],{"class":549},"img\n",[242,681,682,685,687],{"class":139,"line":272},[242,683,684],{"class":285},"  src",[242,686,555],{"class":258},[242,688,689],{"class":265},"\"hero-800.jpg\"\n",[242,691,692,695,697],{"class":139,"line":279},[242,693,694],{"class":285},"  srcset",[242,696,555],{"class":258},[242,698,699],{"class":265},"\"hero-400.jpg 400w, hero-600.jpg 600w, hero-800.jpg 800w,\n",[242,701,702],{"class":139,"line":321},[242,703,704],{"class":265},"          hero-1200.jpg 1200w, hero-1600.jpg 1600w\"\n",[242,706,707,710,712],{"class":139,"line":341},[242,708,709],{"class":285},"  sizes",[242,711,555],{"class":258},[242,713,714],{"class":265},"\"(max-width: 600px) 100vw,\n",[242,716,717],{"class":139,"line":360},[242,718,719],{"class":265},"         (max-width: 1024px) 50vw,\n",[242,721,722],{"class":139,"line":375},[242,723,724],{"class":265},"         720px\"\n",[242,726,727,730,732,735,737,739],{"class":139,"line":395},[242,728,729],{"class":285},"  width",[242,731,555],{"class":258},[242,733,734],{"class":265},"\"1200\"",[242,736,581],{"class":285},[242,738,555],{"class":258},[242,740,741],{"class":265},"\"675\"\n",[242,743,744,747,749],{"class":139,"line":417},[242,745,746],{"class":285},"  alt",[242,748,555],{"class":258},[242,750,751],{"class":265},"\"Quarterly revenue dashboard\"\n",[242,753,754,757,759,762],{"class":139,"line":423},[242,755,756],{"class":285},"  decoding",[242,758,555],{"class":258},[242,760,761],{"class":265},"\"async\"",[242,763,597],{"class":258},[242,765,766],{"class":139,"line":429},[242,767,768],{"class":247},"\u003C!-- trade-off: sizes is a PROMISE about layout. If CSS later changes the slot\n",[242,770,771],{"class":139,"line":435},[242,772,773],{"class":247},"     width and sizes is not updated, the browser keeps choosing against stale\n",[242,775,777],{"class":139,"line":776},14,[242,778,779],{"class":247},"     assumptions and silently over- or under-fetches. Treat sizes as coupled to\n",[242,781,783],{"class":139,"line":782},15,[242,784,785],{"class":247},"     your CSS and lint it when breakpoints change. -->\n",[194,787,789],{"id":788},"_5-art-direction-with-the-picture-element","5. Art Direction with the picture Element",[15,791,792,643,794,796,797,800,801,804,805,807,808,811,812,815,816,643,818,820,821,823,824,827,828,831,832,834,835,37,838,41,840,842],{},[25,793,36],{},[25,795,40],{}," solve ",[228,798,799],{},"resolution switching"," — same image, different pixel counts. They cannot change the ",[228,802,803],{},"crop or aspect ratio"," between breakpoints, which is what art direction requires: a wide cinematic hero on desktop that becomes a tight square crop on mobile so the subject stays legible. For that you need ",[25,806,44],{}," with ",[25,809,810],{},"\u003Csource media=\"...\">",". Each ",[25,813,814],{},"\u003Csource>"," carries its own ",[25,817,36],{},[25,819,40],{},", and the browser uses the first ",[25,822,814],{}," whose ",[25,825,826],{},"media"," matches, falling back to the ",[25,829,830],{},"\u003Cimg>",". The inner ",[25,833,830],{}," is mandatory — it is the element that actually renders and carries ",[25,836,837],{},"alt",[25,839,642],{},[25,841,646],{},".",[15,844,845,847,848,850,851,853,854,856,857,859,860,862],{},[25,846,44],{}," is also the mechanism for format fallback via ",[25,849,218],{},", which overlaps with the codec negotiation in ",[19,852,211],{"href":210},". The two concerns compose: ",[25,855,826],{}," sources handle art direction, ",[25,858,218],{}," sources handle codec selection, and ",[25,861,36],{}," inside each handles resolution.",[233,864,866],{"className":532,"code":865,"language":534,"meta":238,"style":238},"\u003C!-- picture for art direction: different CROP per breakpoint -->\n\u003Cpicture>\n  \u003Csource media=\"(max-width: 600px)\"\n          srcset=\"hero-square-400.jpg 400w, hero-square-800.jpg 800w\"\n          sizes=\"100vw\">\n  \u003Csource media=\"(min-width: 601px)\"\n          srcset=\"hero-wide-800.jpg 800w, hero-wide-1600.jpg 1600w\"\n          sizes=\"(max-width: 1024px) 100vw, 1100px\">\n  \u003Cimg src=\"hero-wide-800.jpg\" width=\"1600\" height=\"600\"\n       alt=\"Product team at launch\" decoding=\"async\">\n\u003C\u002Fpicture>\n\u003C!-- trade-off: picture\u002Fmedia is verbose and locks crops to fixed breakpoints.\n     If you only need different sizes (not different crops), plain img+srcset+sizes\n     is simpler and lets the browser interpolate freely across the width ladder. -->\n",[25,867,868,873,882,898,908,920,933,942,953,981,1000,1009,1014,1019],{"__ignoreMap":238},[242,869,870],{"class":139,"line":244},[242,871,872],{"class":247},"\u003C!-- picture for art direction: different CROP per breakpoint -->\n",[242,874,875,877,880],{"class":139,"line":251},[242,876,546],{"class":258},[242,878,879],{"class":549},"picture",[242,881,597],{"class":258},[242,883,884,887,890,893,895],{"class":139,"line":272},[242,885,886],{"class":258},"  \u003C",[242,888,889],{"class":549},"source",[242,891,892],{"class":285}," media",[242,894,555],{"class":258},[242,896,897],{"class":265},"\"(max-width: 600px)\"\n",[242,899,900,903,905],{"class":139,"line":279},[242,901,902],{"class":285},"          srcset",[242,904,555],{"class":258},[242,906,907],{"class":265},"\"hero-square-400.jpg 400w, hero-square-800.jpg 800w\"\n",[242,909,910,913,915,918],{"class":139,"line":321},[242,911,912],{"class":285},"          sizes",[242,914,555],{"class":258},[242,916,917],{"class":265},"\"100vw\"",[242,919,597],{"class":258},[242,921,922,924,926,928,930],{"class":139,"line":341},[242,923,886],{"class":258},[242,925,889],{"class":549},[242,927,892],{"class":285},[242,929,555],{"class":258},[242,931,932],{"class":265},"\"(min-width: 601px)\"\n",[242,934,935,937,939],{"class":139,"line":360},[242,936,902],{"class":285},[242,938,555],{"class":258},[242,940,941],{"class":265},"\"hero-wide-800.jpg 800w, hero-wide-1600.jpg 1600w\"\n",[242,943,944,946,948,951],{"class":139,"line":375},[242,945,912],{"class":285},[242,947,555],{"class":258},[242,949,950],{"class":265},"\"(max-width: 1024px) 100vw, 1100px\"",[242,952,597],{"class":258},[242,954,955,957,959,961,963,966,969,971,974,976,978],{"class":139,"line":395},[242,956,886],{"class":258},[242,958,57],{"class":549},[242,960,552],{"class":285},[242,962,555],{"class":258},[242,964,965],{"class":265},"\"hero-wide-800.jpg\"",[242,967,968],{"class":285}," width",[242,970,555],{"class":258},[242,972,973],{"class":265},"\"1600\"",[242,975,581],{"class":285},[242,977,555],{"class":258},[242,979,980],{"class":265},"\"600\"\n",[242,982,983,986,988,991,994,996,998],{"class":139,"line":417},[242,984,985],{"class":285},"       alt",[242,987,555],{"class":258},[242,989,990],{"class":265},"\"Product team at launch\"",[242,992,993],{"class":285}," decoding",[242,995,555],{"class":258},[242,997,761],{"class":265},[242,999,597],{"class":258},[242,1001,1002,1005,1007],{"class":139,"line":423},[242,1003,1004],{"class":258},"\u003C\u002F",[242,1006,879],{"class":549},[242,1008,597],{"class":258},[242,1010,1011],{"class":139,"line":429},[242,1012,1013],{"class":247},"\u003C!-- trade-off: picture\u002Fmedia is verbose and locks crops to fixed breakpoints.\n",[242,1015,1016],{"class":139,"line":435},[242,1017,1018],{"class":247},"     If you only need different sizes (not different crops), plain img+srcset+sizes\n",[242,1020,1021],{"class":139,"line":776},[242,1022,1023],{"class":247},"     is simpler and lets the browser interpolate freely across the width ladder. -->\n",[194,1025,1027],{"id":1026},"deconstructing-the-browsers-selection-algorithm","Deconstructing the Browser's Selection Algorithm",[15,1029,1030,1031,1034,1035,1037,1038,1043,1044,1046,1047,1050,1051,1054,1055,1057,1058,1061,1062,1065,1066,1068,1069,1071],{},"Understanding ",[228,1032,1033],{},"why"," the browser chose a file is the difference between guessing and fixing. With ",[25,1036,408],{}," descriptors the selection proceeds in three deterministic phases. First, ",[464,1039,1040,1041],{},"resolve ",[25,1042,40],{},": the browser walks the ",[25,1045,40],{}," list, evaluates each media condition against the current viewport, and takes the length from the first match — this yields the slot width in CSS pixels. A ",[25,1048,1049],{},"vw"," length is resolved against the layout viewport; an absolute length is used as-is. Second, ",[464,1052,1053],{},"apply density",": it multiplies that CSS width by the effective ",[25,1056,499],{}," (2 on most modern phones and retina laptops, sometimes 3 on flagship phones) to get the required ",[228,1059,1060],{},"device"," pixel width. Third, ",[464,1063,1064],{},"select the candidate",": it picks the smallest ",[25,1067,36],{}," entry whose ",[25,1070,408],{}," value is greater than or equal to the required width.",[15,1073,1074,1075,1078,1079,1081,1082,1085,1086,1088],{},"Two browser behaviors surprise engineers. First, the choice is made ",[228,1076,1077],{},"early",", during preload scanning, often before CSS has fully applied — which is why ",[25,1080,40],{}," exists at all, because the browser cannot yet measure the real slot. Second, browsers are permitted to choose a ",[228,1083,1084],{},"larger"," candidate than strictly required when the network is fast or the image is cached, and to choose more conservatively on slow connections via the Network Information API. They will never choose smaller than required, so an under-provisioned ",[25,1087,40],{}," always causes blur, never savings. Once a candidate is chosen for a given DPR it is typically cached and reused, so resizing the window down does not re-fetch a smaller file. This is why testing at multiple discrete widths with a fresh load matters more than dragging the window.",[15,1090,1091,1092,60,1095,1097,1098,1101,1102,1105,1106,1108,1109,1112,1113,1116],{},"It also explains a class of bug that looks like a framework defect but is really the spec working as designed. Because selection runs against the ",[228,1093,1094],{},"resolved",[25,1096,40],{}," length and the ",[228,1099,1100],{},"current"," DPR at request time, a slot whose width depends on JavaScript-applied classes, late-loading fonts, or a layout that only settles after hydration can have its image requested against a transient slot width. The preload scanner fires while the document is still ",[25,1103,1104],{},"100vw"," wide and commits to a candidate before your grid collapses the image into a sidebar — so the byte cost is locked in before the final layout exists. The defensible mitigation is to keep the layout that determines image width stable and declarative (CSS, not JS) above the fold, and to make ",[25,1107,40],{}," describe the ",[228,1110,1111],{},"settled"," layout rather than the initial paint. When the two genuinely diverge, an explicit ",[25,1114,1115],{},"\u003Clink rel=\"preload\" as=\"image\" imagesrcset=... imagesizes=...>"," lets you state the intended candidate to the scanner directly instead of leaving it to infer from a half-built DOM.",[194,1118,1120],{"id":1119},"advanced-diagnostics-and-framework-edge-cases","Advanced Diagnostics and Framework Edge Cases",[15,1122,1123,1124,1127,1128,1131,1132,1134,1135,1137,1138,1140,1141,1143,1144,1146,1147,1149,1150,1152,1153,1155,1156,1158],{},"Component frameworks abstract this markup behind image components — Next.js ",[25,1125,1126],{},"\u003CImage>",", Nuxt ",[25,1129,1130],{},"\u003CNuxtImg>",", Astro ",[25,1133,1126],{}," — which auto-generate ",[25,1136,36],{}," and a default ",[25,1139,40],{},". The default ",[25,1142,40],{}," is almost always ",[25,1145,1104],{},", which is correct for full-bleed heroes and badly wrong for constrained content images, causing systematic over-fetching across an entire app. Always pass an explicit ",[25,1148,40],{}," prop that matches the component's real layout slot; this single prop is the highest-impact image optimization in most framework codebases. Inspect the generated ",[25,1151,830],{}," in DevTools to confirm the ",[25,1154,36],{}," ladder and ",[25,1157,40],{}," are what you intended.",[15,1160,1161,1162,1165,1166,1169,1170,1172,1173,1177],{},"A subtler failure mode is the ",[25,1163,1164],{},"currentSrc"," mismatch. Read ",[25,1167,1168],{},"document.querySelector('img').currentSrc"," in the console to see exactly which candidate the browser actually selected — if it disagrees with your mental model, your ",[25,1171,40],{}," is the culprit. Watch also for DPR edge cases on zoomed desktop browsers and Windows display scaling, where effective DPR can be 1.25 or 1.5, nudging the browser up a ladder step. For the LCP image specifically, responsive selection interacts with priority: the right-sized file still needs to be discovered and fetched early, which is where ",[19,1174,1176],{"href":1175},"\u002Fimage-media-optimization\u002Fimage-cdns-and-fetchpriority\u002F","image CDNs and fetchpriority"," come in to ensure the chosen candidate is requested with high priority rather than waiting behind the preload scanner's default ordering.",[194,1179,1181],{"id":1180},"validation-and-performance-budgeting","Validation and Performance Budgeting",[15,1183,1184,1185,1187],{},"Validation closes the loop opened by your baseline. Re-run the three-viewport measurement and confirm the transferred width now tracks the device-pixel requirement at each breakpoint. The concrete budget: on a 360px phone at DPR 2, the chosen image should be the ~800w candidate (≈40–70KB for a JPEG), not the 1600w file (≈180KB+). On desktop in a 720px slot at DPR 1, expect the 800w candidate; at DPR 2, the 1200–1600w range. Any phone load pulling a >1000w file is a ",[25,1186,40],{}," bug.",[15,1189,1190,1191,643,1194,1197,1198,1200,1201,1203],{},"Enforce this in CI rather than relying on manual checks. Lighthouse flags ",[25,1192,1193],{},"uses-responsive-images",[25,1195,1196],{},"uses-optimized-images"," audits; assert against them so a regression — someone deletes a ",[25,1199,40],{}," prop or changes a layout without updating ",[25,1202,40],{}," — fails the build. Pair this with a hard byte budget on the LCP image, since responsive over-fetching is a leading cause of LCP regressions that pass functional tests.",[233,1205,1209],{"className":1206,"code":1207,"language":1208,"meta":238,"style":238},"language-json shiki shiki-themes github-light-high-contrast github-light-high-contrast github-light-high-contrast","{\n  \"ci\": {\n    \"assert\": {\n      \"assertions\": {\n        \"uses-responsive-images\": [\"error\", { \"minScore\": 1 }],\n        \"uses-optimized-images\": [\"error\", { \"minScore\": 1 }],\n        \"largest-contentful-paint\": [\"error\", { \"maxNumericValue\": 2500 }]\n      }\n    }\n  }\n}\n","json",[25,1210,1211,1216,1224,1231,1238,1263,1282,1304,1309,1314,1319],{"__ignoreMap":238},[242,1212,1213],{"class":139,"line":244},[242,1214,1215],{"class":258},"{\n",[242,1217,1218,1221],{"class":139,"line":251},[242,1219,1220],{"class":549},"  \"ci\"",[242,1222,1223],{"class":258},": {\n",[242,1225,1226,1229],{"class":139,"line":272},[242,1227,1228],{"class":549},"    \"assert\"",[242,1230,1223],{"class":258},[242,1232,1233,1236],{"class":139,"line":279},[242,1234,1235],{"class":549},"      \"assertions\"",[242,1237,1223],{"class":258},[242,1239,1240,1243,1246,1249,1252,1255,1258,1260],{"class":139,"line":321},[242,1241,1242],{"class":549},"        \"uses-responsive-images\"",[242,1244,1245],{"class":258},": [",[242,1247,1248],{"class":265},"\"error\"",[242,1250,1251],{"class":258},", { ",[242,1253,1254],{"class":549},"\"minScore\"",[242,1256,1257],{"class":258},": ",[242,1259,72],{"class":285},[242,1261,1262],{"class":258}," }],\n",[242,1264,1265,1268,1270,1272,1274,1276,1278,1280],{"class":139,"line":341},[242,1266,1267],{"class":549},"        \"uses-optimized-images\"",[242,1269,1245],{"class":258},[242,1271,1248],{"class":265},[242,1273,1251],{"class":258},[242,1275,1254],{"class":549},[242,1277,1257],{"class":258},[242,1279,72],{"class":285},[242,1281,1262],{"class":258},[242,1283,1284,1287,1289,1291,1293,1296,1298,1301],{"class":139,"line":360},[242,1285,1286],{"class":549},"        \"largest-contentful-paint\"",[242,1288,1245],{"class":258},[242,1290,1248],{"class":265},[242,1292,1251],{"class":258},[242,1294,1295],{"class":549},"\"maxNumericValue\"",[242,1297,1257],{"class":258},[242,1299,1300],{"class":285},"2500",[242,1302,1303],{"class":258}," }]\n",[242,1305,1306],{"class":139,"line":375},[242,1307,1308],{"class":258},"      }\n",[242,1310,1311],{"class":139,"line":395},[242,1312,1313],{"class":258},"    }\n",[242,1315,1316],{"class":139,"line":417},[242,1317,1318],{"class":258},"  }\n",[242,1320,1321],{"class":139,"line":423},[242,1322,420],{"class":258},[15,1324,1325],{},[228,1326,1327,1328,1330,1331,1333,1334,1336],{},"Use ",[25,1329,1193],{}," to catch missing or wrong ",[25,1332,36],{},"\u002F",[25,1335,40],{}," before merge.",[194,1338,1340],{"id":1339},"related","Related",[1342,1343,1344,1351,1357,1363,1370],"ul",{},[1345,1346,1347,1350],"li",{},[19,1348,1349],{"href":31},"Measuring LCP with Chrome DevTools"," — confirm that right-sizing the hero actually moved the loading metric that matters.",[1345,1352,1353,1356],{},[19,1354,1355],{"href":210},"Serving AVIF and WebP with fallbacks"," — layer modern codec negotiation on top of the resolution ladder for compounding byte savings.",[1345,1358,1359,1362],{},[19,1360,1361],{"href":1175},"Image CDNs and fetchpriority"," — generate the width ladder on demand and ensure the chosen candidate fetches early.",[1345,1364,1365,1369],{},[19,1366,1368],{"href":1367},"\u002Fimage-media-optimization\u002Fresponsive-images-with-srcset-and-sizes\u002Ffixing-blurry-images-on-high-dpi-displays\u002F","Fixing blurry images on high-DPI displays"," — the diagnostic path when right-sizing goes too far and images render soft on retina screens.",[1345,1371,1372,1374],{},[19,1373,22],{"href":21}," — the broader media-weight strategy this workflow plugs into.",[1376,1377,1379],"script",{"type":1378},"application\u002Fld+json","\n{\n  \"@context\": \"https:\u002F\u002Fschema.org\",\n  \"@type\": \"HowTo\",\n  \"name\": \"Implement responsive images with srcset and sizes\",\n  \"description\": \"A step-by-step workflow to generate a width ladder, choose density vs width descriptors, write a correct sizes attribute, and validate byte savings across viewports.\",\n  \"step\": [\n    { \"@type\": \"HowToStep\", \"position\": 1, \"name\": \"Generate the source ladder\", \"text\": \"Generate image derivatives at 1.5x width steps (400-1600) in modern formats using a build-time tool.\", \"url\": \"https:\u002F\u002Ffrontend-performance.com\u002Fimage-media-optimization\u002Fresponsive-images-with-srcset-and-sizes\u002F#1-environment-setup-and-source-asset-generation\" },\n    { \"@type\": \"HowToStep\", \"position\": 2, \"name\": \"Capture a byte baseline\", \"text\": \"Measure transferred vs rendered image size at 360px, 768px, and 1440px under Fast 3G throttling.\", \"url\": \"https:\u002F\u002Ffrontend-performance.com\u002Fimage-media-optimization\u002Fresponsive-images-with-srcset-and-sizes\u002F#2-capture-a-byte-baseline-across-viewports\" },\n    { \"@type\": \"HowToStep\", \"position\": 3, \"name\": \"Choose descriptor type\", \"text\": \"Use x descriptors for fixed-size images and w descriptors for fluid images.\", \"url\": \"https:\u002F\u002Ffrontend-performance.com\u002Fimage-media-optimization\u002Fresponsive-images-with-srcset-and-sizes\u002F#3-isolate-the-bottleneck-density-vs-width-descriptors\" },\n    { \"@type\": \"HowToStep\", \"position\": 4, \"name\": \"Write srcset and sizes\", \"text\": \"Pair a w-descriptor ladder with a sizes attribute that mirrors the real CSS layout slot.\", \"url\": \"https:\u002F\u002Ffrontend-performance.com\u002Fimage-media-optimization\u002Fresponsive-images-with-srcset-and-sizes\u002F#4-apply-the-fix-srcset--sizes-for-fluid-images\" },\n    { \"@type\": \"HowToStep\", \"position\": 5, \"name\": \"Add art direction where needed\", \"text\": \"Use the picture element with media sources when crop or aspect ratio must change per breakpoint.\", \"url\": \"https:\u002F\u002Ffrontend-performance.com\u002Fimage-media-optimization\u002Fresponsive-images-with-srcset-and-sizes\u002F#5-art-direction-with-the-picture-element\" },\n    { \"@type\": \"HowToStep\", \"position\": 6, \"name\": \"Validate and budget\", \"text\": \"Re-measure across viewports and assert responsive-image audits and an LCP budget in CI.\", \"url\": \"https:\u002F\u002Ffrontend-performance.com\u002Fimage-media-optimization\u002Fresponsive-images-with-srcset-and-sizes\u002F#validation-and-performance-budgeting\" }\n  ]\n}\n",[1376,1381,1382],{"type":1378},"\n{\n  \"@context\": \"https:\u002F\u002Fschema.org\",\n  \"@type\": \"TechArticle\",\n  \"headline\": \"Responsive Images with srcset and sizes: A Diagnostic Workflow for Right-Sized Delivery\",\n  \"description\": \"How the browser selects a srcset candidate from sizes and devicePixelRatio, when to use the picture element, and how to stop over-downloading on mobile.\",\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\u002Fresponsive-images-with-srcset-and-sizes\u002F\" }\n}\n",[1376,1384,1385],{"type":1378},"\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\": \"Responsive Images with srcset and sizes\", \"item\": \"https:\u002F\u002Ffrontend-performance.com\u002Fimage-media-optimization\u002Fresponsive-images-with-srcset-and-sizes\u002F\" }\n  ]\n}\n",[1387,1388,1389],"style",{},"html pre.shiki code .sIIH1, html code.shiki .sIIH1{--shiki-default:#66707B;--shiki-dark:#66707B;--shiki-light:#66707B}html pre.shiki code .sP5qI, html code.shiki .sP5qI{--shiki-default:#A0111F;--shiki-dark:#A0111F;--shiki-light:#A0111F}html pre.shiki code .syybb, html code.shiki .syybb{--shiki-default:#0E1116;--shiki-dark:#0E1116;--shiki-light:#0E1116}html pre.shiki code .s-_DF, html code.shiki .s-_DF{--shiki-default:#032563;--shiki-dark:#032563;--shiki-light:#032563}html pre.shiki code .sf6mN, html code.shiki .sf6mN{--shiki-default:#023B95;--shiki-dark:#023B95;--shiki-light:#023B95}html pre.shiki code .ssM3C, html code.shiki .ssM3C{--shiki-default:#622CBC;--shiki-dark:#622CBC;--shiki-light:#622CBC}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 .s-fAs, html code.shiki .s-fAs{--shiki-default:#024C1A;--shiki-dark:#024C1A;--shiki-light:#024C1A}",{"title":238,"searchDepth":251,"depth":251,"links":1391},[1392,1393,1394,1395,1396,1397,1398,1399,1400],{"id":196,"depth":251,"text":197},{"id":441,"depth":251,"text":442},{"id":478,"depth":251,"text":479},{"id":615,"depth":251,"text":616},{"id":788,"depth":251,"text":789},{"id":1026,"depth":251,"text":1027},{"id":1119,"depth":251,"text":1120},{"id":1180,"depth":251,"text":1181},{"id":1339,"depth":251,"text":1340},"A diagnostic workflow for srcset, sizes, and picture: how the browser picks a candidate and how to stop over-downloading on mobile.","md",{"slug":1404,"type":1405,"breadcrumb":1406,"datePublished":1412,"dateModified":1412},"responsive-images-with-srcset-and-sizes","cluster",[1407,1409,1410],{"name":1408,"url":1333},"Home",{"name":22,"url":21},{"name":5,"url":1411},"\u002Fimage-media-optimization\u002Fresponsive-images-with-srcset-and-sizes\u002F","2026-06-18","\u002Fimage-media-optimization\u002Fresponsive-images-with-srcset-and-sizes",{"title":5,"description":1415},"Deliver the right-sized image to every device using srcset width descriptors, the sizes attribute, and picture. Cut mobile bytes and protect LCP.","image-media-optimization\u002Fresponsive-images-with-srcset-and-sizes\u002Findex","rve8Q6NIHPlV0H-1yCOWRZ6x97NIDMDSxxpRI7CiZrw",[1419,1423],{"title":1420,"path":1421,"stem":1422,"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",{"title":1424,"path":1425,"stem":1426,"children":-1},"Fixing Blurry Images on High-DPI Displays","\u002Fimage-media-optimization\u002Fresponsive-images-with-srcset-and-sizes\u002Ffixing-blurry-images-on-high-dpi-displays","image-media-optimization\u002Fresponsive-images-with-srcset-and-sizes\u002Ffixing-blurry-images-on-high-dpi-displays\u002Findex",1782237170880]