ResizeObserver API
React to element size changes not just window resize using the ResizeObserver API for responsive components and dynamic layouts.
Overview
Key Points
- •`new ResizeObserver(callback)` callback receives `ResizeObserverEntry[]`
- •`entry.contentRect` the element's content area (width, height, top, left)
- •`entry.borderBoxSize` / `entry.contentBoxSize` newer API returning arrays of sizes
- •`observer.observe(el, { box: "border-box" })` observe border box instead of content box
- •`observer.unobserve(el)` / `observer.disconnect()` stop observing
- •The callback fires immediately when observation starts (initial measurement)
- •ResizeObserver uses requestAnimationFrame timing won't cause infinite loops from CSS changes
Examples
Responsive chart that redraws when its container resizes
<div id="chart-container" style="width:100%; height:300px;">
<canvas id="chart"></canvas>
</div>
<script>
const container = document.getElementById('chart-container');
const canvas = document.getElementById('chart');
const resizeObserver = new ResizeObserver((entries) => {
for (const entry of entries) {
const { width, height } = entry.contentRect;
canvas.width = width;
canvas.height = height;
drawChart(canvas, width, height);
}
});
resizeObserver.observe(container);
function drawChart(canvas, width, height) {
const ctx = canvas.getContext('2d');
ctx.clearRect(0, 0, width, height);
// ... drawing logic
}
</script>The callback fires immediately on observe(), giving you the initial size without a separate measurement.
Element-level responsive design (component queries)
<style>
.card { padding: 1rem; }
.card[data-size="small"] { flex-direction: column; }
.card[data-size="large"] { flex-direction: row; display: flex; }
</style>
<div class="card" id="product-card">
<img src="product.jpg" alt="Product">
<div class="info">
<h3>Product Name</h3>
<button>Buy Now</button>
</div>
</div>
<script>
const cardObserver = new ResizeObserver((entries) => {
entries.forEach(entry => {
const width = entry.contentRect.width;
entry.target.dataset.size = width < 400 ? 'small' : 'large';
});
});
document.querySelectorAll('.card').forEach(card => cardObserver.observe(card));
</script>This is the "container query" pattern in JS. Modern CSS @container does this without JS.
Tracking editor dimensions with ResizeObserver
<div
id="editor"
contenteditable="true"
role="textbox"
aria-multiline="true"
style="min-height: 100px; border: 1px solid #ccc; padding: 8px;"
>
Type here...
</div>
<div id="line-count">1 line</div>
<script>
const editor = document.getElementById('editor');
const editorObserver = new ResizeObserver((entries) => {
const entry = entries[0];
const height = entry.contentRect.height;
const lineHeight = parseFloat(getComputedStyle(editor).lineHeight);
const lines = Math.round(height / lineHeight);
document.getElementById('line-count').textContent = `~${lines} lines`;
});
editorObserver.observe(editor);
</script>Common Mistakes
- Modifying the observed element's size inside the ResizeObserver callback causes infinite loops; use `requestAnimationFrame` or a flag
- Using window.resize event when you only care about a specific element's size ResizeObserver is more precise and efficient
- Forgetting that `entry.contentRect` is read-only and from a previous frame don't cache it for use after the callback returns
Note
In 2024, CSS Container Queries (`@container`) handle many element-responsive patterns without JavaScript. Use ResizeObserver when you need to react to size changes in JavaScript (canvas, virtual lists, etc.).
Exercises
Write HTML to solve each exercise. Use Preview to see it render in a live iframe, then mark as done.
Completed: 0 / 5 exercises
Knowledge Check
Question 1 of 4