[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"quiz-resize-observer-element-size":3,"verticals":25,"quiz-article-resize-observer-element-size":48},{"id":4,"slug":5,"kind":6,"title":7,"description":8,"config":9,"verticalId":17,"vertical":18,"course":11,"_count":21,"access":22,"attempts":24,"questionCount":10},"019fe776-7d30-70da-a6ad-6487915a330a","resize-observer-element-size","PRACTICE_QUIZ","ResizeObserver: watching an element's own size","Test yourself on how `ResizeObserver` differs from a `window` resize listener, what each `ResizeObserverEntry` property reports, the `box` option, and the two gotchas — the infinite-loop warning and the initial callback.",{"questionCount":10,"timeLimitSec":11,"shuffleQuestions":12,"shuffleOptions":13,"negativeMarking":14,"passScorePct":15,"maxAttempts":11,"revealAnswers":16,"allowFlagging":12,"allowBacktracking":13},6,null,false,true,0,70,"IMMEDIATE","019fe637-3d33-714b-b57f-23e163ffca0c",{"slug":19,"name":20},"dev","Web Development",{"questions":10},{"allowed":13,"reason":23},"FREE",[],[26,36],{"id":17,"slug":19,"name":20,"tagline":27,"description":28,"accentFrom":29,"accentTo":30,"icon":31,"defaultLocale":32,"locales":33,"features":35,"position":14},"Build. Learn. Ship.","Practical courses, engineering-grade articles and open-source tools for people who ship.","violet-500","cyan-400","◇","en",[32,34],"fa",{"courses":13,"paths":13,"articles":13,"exams":12,"flashcards":12,"packages":13,"community":13,"certificates":13,"teams":13,"commerce":13},{"id":37,"slug":38,"name":39,"tagline":40,"description":41,"accentFrom":42,"accentTo":29,"icon":43,"defaultLocale":32,"locales":44,"features":46,"position":47},"019fe637-3dc2-754c-8657-0f175bfee7c6","lang","Languages","Learn a language the way you learn a codebase.","Structured paths, listening drills and spaced repetition that actually sticks.","amber-400","⌘",[32,34,45],"es",{"courses":13,"paths":13,"articles":12,"exams":12,"flashcards":12,"packages":12,"community":13,"certificates":12,"teams":12,"commerce":13},2,{"id":49,"slug":5,"title":50,"subtitle":11,"excerpt":51,"coverUrl":52,"locale":32,"readingMinutes":53,"publishedAt":54,"viewCount":55,"likeCount":14,"commentCount":14,"author":56,"vertical":61,"topic":62,"tags":65,"_count":77,"playground":79,"body":81,"bodyMd":220,"seo":221,"translationGroupId":225,"thread":226,"assessments":228,"translations":231,"quiz":233},"019fe660-8482-75bc-ab16-8e86f92efc09","You're watching `window` to detect element size changes. `ResizeObserver` watches the element itself.","A `window` resize listener only fires when the viewport changes. ResizeObserver fires whenever the element's own box changes — from content loading, parent reflow, container queries, or anything else.","\u002Fmedia\u002Fcovers\u002Fresize-observer-element-size.png",4,"2026-07-26T08:32:44.347Z",52,{"id":57,"name":58,"username":59,"avatarUrl":11,"headline":60},"019fe637-3c25-7088-9034-39c9f15dc3c8","Parsa Jiravand","parsa","Frontend engineer · building bestpractic",{"slug":19,"name":20,"accentFrom":29,"accentTo":30},{"slug":63,"name":64},"javascript","JavaScript",[66,68,71,74],{"slug":63,"name":67,"color":11},"Javascript",{"slug":69,"name":70,"color":11},"webdev","Webdev",{"slug":72,"name":73,"color":11},"frontend","Frontend",{"slug":75,"name":76,"color":11},"performance","Performance",{"assessments":78},1,{"slug":5,"title":80},"ResizeObserver — interactive playground",{"blocks":82,"version":78},[83,87,93,96,100,103,107,110,114,117,121,124,128,131,134,137,140,143,146,149,152,156,159,162,165,169,172,175,178,181,184,187,190,193,196,199,202,205,208,211],{"id":84,"html":85,"type":86},"b1","\u003Cp>Here is something every frontend developer has written:\u003C\u002Fp>","paragraph",{"id":88,"code":89,"type":90,"language":91,"highlight":92},"b2","window.addEventListener('resize', () => {\n  const width = container.getBoundingClientRect().width;\n  updateLayout(width);\n});","code","js",[],{"id":94,"html":95,"type":86},"b3","\u003Cp>It works when the user drags the browser window. It does not work when the container changes size for any other reason: a parent flex layout shifts because sibling content loaded, a container query breakpoint triggers a CSS change, an image inside it loads and forces reflow, or you programmatically modify its children. \u003Ccode>window\u003C\u002Fcode> resize fires for one of the many things that can resize an element. \u003Ccode>ResizeObserver\u003C\u002Fcode> was built for the rest.\u003C\u002Fp>",{"id":97,"html":98,"text":98,"type":99,"level":47},"b4","How ResizeObserver works","heading",{"id":101,"html":102,"type":86},"b5","\u003Cp>You pass a callback and tell it which elements to watch:\u003C\u002Fp>",{"id":104,"code":105,"type":90,"language":91,"highlight":106},"b6","const observer = new ResizeObserver((entries) => {\n  for (const entry of entries) {\n    const { width, height } = entry.contentRect;\n    updateLayout(width, height);\n  }\n});\n\nobserver.observe(container);",[],{"id":108,"html":109,"type":86},"b7","\u003Cp>The callback fires whenever the observed element&#39;s size changes — regardless of why it changed. Stop watching with \u003Ccode>unobserve\u003C\u002Fcode> or \u003Ccode>disconnect\u003C\u002Fcode>:\u003C\u002Fp>",{"id":111,"code":112,"type":90,"language":91,"highlight":113},"b8","observer.unobserve(container);  \u002F\u002F stop watching one element\nobserver.disconnect();           \u002F\u002F stop watching all at once",[],{"id":115,"html":116,"type":86},"b9","\u003Cp>The callback receives an array of entries because a single ResizeObserver can observe multiple elements, and the browser may batch several size changes into one notification. Always loop over all entries.\u003C\u002Fp>",{"id":118,"html":119,"text":120,"type":99,"level":47},"b10","\u003Ccode>contentRect\u003C\u002Fcode>, \u003Ccode>contentBoxSize\u003C\u002Fcode>, and \u003Ccode>borderBoxSize\u003C\u002Fcode>","contentRect, contentBoxSize, and borderBoxSize",{"id":122,"html":123,"type":86},"b11","\u003Cp>Each entry gives you three ways to read the new dimensions:\u003C\u002Fp>",{"id":125,"code":126,"type":90,"language":91,"highlight":127},"b12","const observer = new ResizeObserver((entries) => {\n  for (const entry of entries) {\n    \u002F\u002F Older property — still works, content area only\n    const { width, height } = entry.contentRect;\n\n    \u002F\u002F Logical dimensions — inlineSize maps to width in horizontal writing modes\n    const { inlineSize, blockSize } = entry.contentBoxSize[0];\n\n    \u002F\u002F Includes padding and border, like offsetWidth\n    const { inlineSize: borderWidth } = entry.borderBoxSize[0];\n  }\n});",[],{"id":129,"html":130,"type":86},"b13","\u003Cp>\u003Ccode>contentBoxSize\u003C\u002Fcode> and \u003Ccode>borderBoxSize\u003C\u002Fcode> use logical dimension names (\u003Ccode>inlineSize\u003C\u002Fcode> for width in LTR\u002FRTL layouts, \u003Ccode>blockSize\u003C\u002Fcode> for height) so the same code works correctly in vertical writing modes. For standard horizontal layouts, \u003Ccode>contentBoxSize[0].inlineSize\u003C\u002Fcode> is the content width and \u003Ccode>borderBoxSize[0].inlineSize\u003C\u002Fcode> is what \u003Ccode>offsetWidth\u003C\u002Fcode> would return — without the layout read.\u003C\u002Fp>",{"id":132,"html":133,"type":86},"b14","\u003C!-- playground:start -->",{"id":135,"html":136,"text":136,"type":99,"level":47},"b15","🎮 Try it yourself",{"id":138,"html":139,"type":86},"b16","\u003Cp>\u003Cstrong>\u003Ca href=\"https:\u002F\u002Fdaily-post-dev.netlify.app\u002Fposts\u002F2026-07-26-resize-observer-element-size\u002Fplayground\u002F\">▶️ Open the interactive playground →\u003C\u002Fa>\u003C\u002Fstrong>\u003C\u002Fp>",{"id":141,"html":142,"type":86},"b17","\u003Cp>\u003Cem>Runs right in your browser — poke at it and watch the concept react live.\u003C\u002Fem>\u003C\u002Fp>",{"id":144,"html":145,"type":86},"b18","\u003C!-- playground:end -->",{"id":147,"html":148,"text":148,"type":99,"level":47},"b19","The canvas resize pattern",{"id":150,"html":151,"type":86},"b20","\u003Cp>The classic use case: keeping a \u003Ccode>&lt;canvas&gt;\u003C\u002Fcode> pixel-perfect as its container resizes.\u003C\u002Fp>",{"id":153,"code":154,"type":90,"language":91,"highlight":155},"b21","const canvas = document.querySelector('canvas');\nconst ctx = canvas.getContext('2d');\n\nconst observer = new ResizeObserver((entries) => {\n  for (const entry of entries) {\n    const { inlineSize: width, blockSize: height } = entry.contentBoxSize[0];\n    const dpr = window.devicePixelRatio ?? 1;\n\n    canvas.width = Math.round(width * dpr);\n    canvas.height = Math.round(height * dpr);\n    ctx.scale(dpr, dpr);\n    draw();\n  }\n});\n\nobserver.observe(canvas);",[],{"id":157,"html":158,"type":86},"b22","\u003Cp>With a \u003Ccode>window\u003C\u002Fcode> resize listener, you miss every canvas resize that happens without the viewport changing — panels collapsing, sidebars toggling, content loading. With \u003Ccode>ResizeObserver\u003C\u002Fcode>, the canvas always matches its container, at the right pixel density.\u003C\u002Fp>",{"id":160,"html":161,"text":161,"type":99,"level":47},"b23","No debouncing needed",{"id":163,"html":164,"type":86},"b24","\u003Cp>Unlike scroll listeners or window resize handlers, \u003Ccode>ResizeObserver\u003C\u002Fcode> does not fire on every animation frame. The browser batches and schedules callbacks after layout and before paint, at most once per frame. You do not need to debounce or throttle it:\u003C\u002Fp>",{"id":166,"code":167,"type":90,"language":91,"highlight":168},"b25","\u002F\u002F Don't do this — delays the response for no benefit\nconst observer = new ResizeObserver(debounce((entries) => { ... }, 100));\n\n\u002F\u002F The browser already handles the batching\nconst observer = new ResizeObserver((entries) => { ... });",[],{"id":170,"html":171,"type":86},"b26","\u003Cp>The debounce would add latency without removing any work, because \u003Ccode>ResizeObserver\u003C\u002Fcode> already delivers at the right moment.\u003C\u002Fp>",{"id":173,"html":174,"text":174,"type":99,"level":47},"b27","Avoiding the infinite-loop trap",{"id":176,"html":177,"type":86},"b28","\u003Cp>If your callback changes the observed element&#39;s size, it triggers another callback, which changes the size again. The browser detects this and logs \u003Ccode>ResizeObserver loop completed with undelivered notifications\u003C\u002Fcode>. The fix: do not write layout-affecting styles back to the element being observed inside the callback. If you need to respond to an element&#39;s width by applying a class or attribute (the element query pattern), make sure those class changes don&#39;t affect the element&#39;s own box dimensions — or defer the write with \u003Ccode>requestAnimationFrame\u003C\u002Fcode> so it lands outside the current resize notification cycle.\u003C\u002Fp>",{"id":179,"html":180,"text":180,"type":99,"level":47},"b29","Browser support",{"id":182,"html":183,"type":86},"b30","\u003Cp>\u003Ccode>ResizeObserver\u003C\u002Fcode> is \u003Cstrong>Baseline 2020\u003C\u002Fstrong>: Chrome 64 (January 2018), Firefox 69 (September 2019), Safari 13.1 (March 2020). Every modern browser ships it. There is no equivalent in Node.js (no layout engine), but for anything running in a browser, no polyfill is needed.\u003C\u002Fp>",{"id":185,"html":186,"type":86},"b31","\u003C!-- quiz:start -->",{"id":188,"html":189,"text":189,"type":99,"level":47},"b32","🧠 Test yourself",{"id":191,"html":192,"type":86},"b33","\u003Cp>Think it clicked? \u003Cstrong>\u003Ca href=\"https:\u002F\u002Fdaily-post-dev.netlify.app\u002Fquiz\u002Ftake.html?post=2026-07-26-resize-observer-element-size\">Take the 6-question quiz →\u003C\u002Fa>\u003C\u002Fstrong>\u003C\u002Fp>",{"id":194,"html":195,"type":86},"b34","\u003Cp>\u003Cem>Instant feedback, a hint on every question, and an explanation for each answer — right or wrong.\u003C\u002Fem>\u003C\u002Fp>",{"id":197,"html":198,"type":86},"b35","\u003C!-- quiz:end -->",{"id":200,"html":201,"text":201,"type":99,"level":47},"b36","The takeaway",{"id":203,"html":204,"type":86},"b37","\u003Cp>If your codebase has \u003Ccode>container.getBoundingClientRect()\u003C\u002Fcode> inside a \u003Ccode>window\u003C\u002Fcode> resize handler, it&#39;s solving the wrong problem with the wrong tool. \u003Ccode>window\u003C\u002Fcode> resize is a proxy for element resize that silently misses everything that doesn&#39;t involve the viewport. \u003Ccode>ResizeObserver\u003C\u002Fcode> watches the element. The callback fires when the element&#39;s box changes, and it hands you the new dimensions directly — no follow-up layout read required.\u003C\u002Fp>",{"id":206,"type":207},"b38","divider",{"id":209,"html":210,"type":86},"b39","\u003Cp>\u003Cem>Thanks for reading! Let&#39;s stay connected:\u003C\u002Fem>\u003C\u002Fp>",{"id":212,"type":213,"items":214,"ordered":12},"b40","list",[215,216,217,218,219],"⭐ \u003Cstrong>GitHub\u003C\u002Fstrong> — follow me and star the projects: \u003Ca href=\"https:\u002F\u002Fgithub.com\u002Fparsajiravand\">github.com\u002Fparsajiravand\u003C\u002Fa>","💬 \u003Cstrong>Discord\u003C\u002Fstrong> — join the frontend best-practices community: \u003Ca href=\"https:\u002F\u002Fdiscord.gg\u002Fd9KRhuAwQ\">discord.gg\u002Fd9KRhuAwQ\u003C\u002Fa>","📸 \u003Cstrong>Instagram\u003C\u002Fstrong> — frontend best practices, daily: \u003Ca href=\"https:\u002F\u002Fwww.instagram.com\u002Fbestpractice___\u002F\">@bestpractice___\u003C\u002Fa>","💼 \u003Cstrong>LinkedIn\u003C\u002Fstrong> — \u003Ca href=\"https:\u002F\u002Fwww.linkedin.com\u002Fin\u002Fparsa-jiravand\u002F\">linkedin.com\u002Fin\u002Fparsa-jiravand\u003C\u002Fa>","✉️ \u003Cstrong>Email\u003C\u002Fstrong> (work &amp; contract inquiries): \u003Ca href=\"mailto:bestpractice2026@gmail.com\">bestpractice2026@gmail.com\u003C\u002Fa>","Here is something every frontend developer has written:\n\n```js\nwindow.addEventListener('resize', () => {\n  const width = container.getBoundingClientRect().width;\n  updateLayout(width);\n});\n```\n\nIt works when the user drags the browser window. It does not work when the container changes size for any other reason: a parent flex layout shifts because sibling content loaded, a container query breakpoint triggers a CSS change, an image inside it loads and forces reflow, or you programmatically modify its children. `window` resize fires for one of the many things that can resize an element. `ResizeObserver` was built for the rest.\n\n## How ResizeObserver works\n\nYou pass a callback and tell it which elements to watch:\n\n```js\nconst observer = new ResizeObserver((entries) => {\n  for (const entry of entries) {\n    const { width, height } = entry.contentRect;\n    updateLayout(width, height);\n  }\n});\n\nobserver.observe(container);\n```\n\nThe callback fires whenever the observed element's size changes — regardless of why it changed. Stop watching with `unobserve` or `disconnect`:\n\n```js\nobserver.unobserve(container);  \u002F\u002F stop watching one element\nobserver.disconnect();           \u002F\u002F stop watching all at once\n```\n\nThe callback receives an array of entries because a single ResizeObserver can observe multiple elements, and the browser may batch several size changes into one notification. Always loop over all entries.\n\n## `contentRect`, `contentBoxSize`, and `borderBoxSize`\n\nEach entry gives you three ways to read the new dimensions:\n\n```js\nconst observer = new ResizeObserver((entries) => {\n  for (const entry of entries) {\n    \u002F\u002F Older property — still works, content area only\n    const { width, height } = entry.contentRect;\n\n    \u002F\u002F Logical dimensions — inlineSize maps to width in horizontal writing modes\n    const { inlineSize, blockSize } = entry.contentBoxSize[0];\n\n    \u002F\u002F Includes padding and border, like offsetWidth\n    const { inlineSize: borderWidth } = entry.borderBoxSize[0];\n  }\n});\n```\n\n`contentBoxSize` and `borderBoxSize` use logical dimension names (`inlineSize` for width in LTR\u002FRTL layouts, `blockSize` for height) so the same code works correctly in vertical writing modes. For standard horizontal layouts, `contentBoxSize[0].inlineSize` is the content width and `borderBoxSize[0].inlineSize` is what `offsetWidth` would return — without the layout read.\n\n\u003C!-- playground:start -->\n\n## 🎮 Try it yourself\n\n**[▶️ Open the interactive playground →](https:\u002F\u002Fdaily-post-dev.netlify.app\u002Fposts\u002F2026-07-26-resize-observer-element-size\u002Fplayground\u002F)**\n\n_Runs right in your browser — poke at it and watch the concept react live._\n\n\u003C!-- playground:end -->\n\n## The canvas resize pattern\n\nThe classic use case: keeping a `\u003Ccanvas>` pixel-perfect as its container resizes.\n\n```js\nconst canvas = document.querySelector('canvas');\nconst ctx = canvas.getContext('2d');\n\nconst observer = new ResizeObserver((entries) => {\n  for (const entry of entries) {\n    const { inlineSize: width, blockSize: height } = entry.contentBoxSize[0];\n    const dpr = window.devicePixelRatio ?? 1;\n\n    canvas.width = Math.round(width * dpr);\n    canvas.height = Math.round(height * dpr);\n    ctx.scale(dpr, dpr);\n    draw();\n  }\n});\n\nobserver.observe(canvas);\n```\n\nWith a `window` resize listener, you miss every canvas resize that happens without the viewport changing — panels collapsing, sidebars toggling, content loading. With `ResizeObserver`, the canvas always matches its container, at the right pixel density.\n\n## No debouncing needed\n\nUnlike scroll listeners or window resize handlers, `ResizeObserver` does not fire on every animation frame. The browser batches and schedules callbacks after layout and before paint, at most once per frame. You do not need to debounce or throttle it:\n\n```js\n\u002F\u002F Don't do this — delays the response for no benefit\nconst observer = new ResizeObserver(debounce((entries) => { ... }, 100));\n\n\u002F\u002F The browser already handles the batching\nconst observer = new ResizeObserver((entries) => { ... });\n```\n\nThe debounce would add latency without removing any work, because `ResizeObserver` already delivers at the right moment.\n\n## Avoiding the infinite-loop trap\n\nIf your callback changes the observed element's size, it triggers another callback, which changes the size again. The browser detects this and logs `ResizeObserver loop completed with undelivered notifications`. The fix: do not write layout-affecting styles back to the element being observed inside the callback. If you need to respond to an element's width by applying a class or attribute (the element query pattern), make sure those class changes don't affect the element's own box dimensions — or defer the write with `requestAnimationFrame` so it lands outside the current resize notification cycle.\n\n## Browser support\n\n`ResizeObserver` is **Baseline 2020**: Chrome 64 (January 2018), Firefox 69 (September 2019), Safari 13.1 (March 2020). Every modern browser ships it. There is no equivalent in Node.js (no layout engine), but for anything running in a browser, no polyfill is needed.\n\n\u003C!-- quiz:start -->\n\n## 🧠 Test yourself\n\nThink it clicked? **[Take the 6-question quiz →](https:\u002F\u002Fdaily-post-dev.netlify.app\u002Fquiz\u002Ftake.html?post=2026-07-26-resize-observer-element-size)**\n\n_Instant feedback, a hint on every question, and an explanation for each answer — right or wrong._\n\n\u003C!-- quiz:end -->\n\n## The takeaway\n\nIf your codebase has `container.getBoundingClientRect()` inside a `window` resize handler, it's solving the wrong problem with the wrong tool. `window` resize is a proxy for element resize that silently misses everything that doesn't involve the viewport. `ResizeObserver` watches the element. The callback fires when the element's box changes, and it hands you the new dimensions directly — no follow-up layout read required.\n\n---\n\n*Thanks for reading! Let's stay connected:*\n\n- ⭐ **GitHub** — follow me and star the projects: [github.com\u002Fparsajiravand](https:\u002F\u002Fgithub.com\u002Fparsajiravand)\n- 💬 **Discord** — join the frontend best-practices community: [discord.gg\u002Fd9KRhuAwQ](https:\u002F\u002Fdiscord.gg\u002Fd9KRhuAwQ)\n- 📸 **Instagram** — frontend best practices, daily: [@bestpractice___](https:\u002F\u002Fwww.instagram.com\u002Fbestpractice___\u002F)\n- 💼 **LinkedIn** — [linkedin.com\u002Fin\u002Fparsa-jiravand](https:\u002F\u002Fwww.linkedin.com\u002Fin\u002Fparsa-jiravand\u002F)\n- ✉️ **Email** (work & contract inquiries): [bestpractice2026@gmail.com](mailto:bestpractice2026@gmail.com)",{"title":222,"canonical":223,"description":224},"You're watching `window` to detect element size changes. `ResizeObserv","https:\u002F\u002Fbestpractic.org\u002Fblog\u002Fresize-observer-element-size","A `window` resize listener only fires when the viewport changes. ResizeObserver fires whenever the element's own box changes — from content loading, parent reflow, container querie","019fe660-8482-75bc-ab16-93b00caf86e6",{"id":227,"locked":12},"019fe660-8b2c-7119-be27-c80a8895f840",[229],{"id":4,"slug":5,"title":7,"_count":230},{"questions":10},[232],{"locale":32,"slug":5},{"id":4,"slug":5,"title":7,"_count":234,"questionCount":10},{"questions":10}]