Skip to content

Resize Observer — Complete Guide

DodaTech Updated 2026-06-28 7 min read

In this tutorial, you will learn about Resize Observer. We cover key concepts, practical examples, and best practices to help you master this topic.

Resize Observer detects when an element's dimensions change, enabling responsive components and adaptive layouts that react to container size changes.

What You'll Learn

  • How to create a ResizeObserver and observe element resize events
  • How to read the new dimensions from ResizeObserverEntry
  • How to implement responsive components that adapt to container width
  • The difference between ResizeObserver and window resize events

Why It Matters

Responsive Design is not just about the viewport. Components inside a flexible layout need to adapt when their container shrinks or grows. ResizeObserver provides element-level resize detection that window resize events cannot provide.

Real-World Use

  • A dashboard widget reflows its layout when the container narrows
  • A responsive data table switches to a card view on small containers
  • An embedded chart resizes when the parent panel is resized
  • A textarea auto-grows in height as the user types
flowchart LR
  A[Element Resizes] --> B[ResizeObserver Fires]
  B --> C[Read new dimensions]
  C --> D[borderBoxSize]
  C --> E[contentBoxSize]
  C --> F[devicePixelContentBoxSize]
  D --> G[Adjust layout]
  G --> H[Component adapts]

Basic Resize Observation

Watch an element and log its dimensions whenever they change.

const resizable = document.querySelector('.resizable-box');
const sizeDisplay = document.querySelector('.size-display');

const observer = new ResizeObserver(function(entries) {
    for (const entry of entries) {
        const target = entry.target;
        const contentRect = entry.contentRect;

        // contentRect gives width, height, top, left
        console.log('Element resized:', target.className);
        console.log('  New width:', contentRect.width);
        console.log('  New height:', contentRect.height);
        console.log('  Top:', contentRect.top);
        console.log('  Left:', contentRect.left);

        // Update display
        sizeDisplay.textContent = `${Math.round(contentRect.width)} x ${Math.round(contentRect.height)}`;
    }
});

// Start observing
observer.observe(resizable);

// Later: observer.unobserve(resizable);
// observer.disconnect();

Expected output: When the element is resized (by browser window resize, CSS changes, or JavaScript), the console logs the new dimensions. The display updates with the current size.

Using borderBoxSize and contentBoxSize

Modern ResizeObserverEntry provides box-specific size information.

const component = document.querySelector('.responsive-component');

const boxObserver = new ResizeObserver(function(entries) {
    for (const entry of entries) {
        // contentBoxSize: content area (padding-box in some browsers)
        const contentSize = entry.contentBoxSize[0];
        console.log('Content box:', contentSize.inlineSize, contentSize.blockSize);

        // borderBoxSize: border-box (includes padding and border)
        const borderSize = entry.borderBoxSize[0];
        console.log('Border box:', borderSize.inlineSize, borderSize.blockSize);

        // devicePixelContentBoxSize: device pixel resolution
        if (entry.devicePixelContentBoxSize) {
            const deviceSize = entry.devicePixelContentBoxSize[0];
            console.log('Device pixel:', deviceSize.inlineSize, deviceSize.blockSize);
        }

        // Use inlineSize/blockSize (logical properties)
        // instead of width/height for RTL support
        // In horizontal writing: inlineSize = width, blockSize = height

        // contentRect (legacy fallback)
        console.log('contentRect:', entry.contentRect.width, entry.contentRect.height);
    }
});

boxObserver.observe(component);

Expected output: The console shows the component's size in both content-box and border-box measurements. The device pixel box accounts for high-DPI displays.

Responsive Component Pattern

Use ResizeObserver to make components adapt to available space.

class ResponsiveGrid {
    constructor(container) {
        this.container = container;
        this.currentLayout = 'large';

        this.observer = new ResizeObserver((entries) => {
            this.handleResize(entries[0]);
        });

        this.observer.observe(container);
    }

    handleResize(entry) {
        const width = entry.contentRect.width;
        let newLayout;

        if (width > 800) {
            newLayout = 'large';
        } else if (width > 500) {
            newLayout = 'medium';
        } else if (width > 300) {
            newLayout = 'small';
        } else {
            newLayout = 'xsmall';
        }

        if (newLayout !== this.currentLayout) {
            this.currentLayout = newLayout;
            this.applyLayout(newLayout);
        }
    }

    applyLayout(layout) {
        this.container.className = `grid grid-${layout}`;
        console.log('Layout changed to:', layout);

        switch (layout) {
            case 'large':
                // 3 columns, full detail
                this.container.style.gridTemplateColumns = 'repeat(3, 1fr)';
                break;
            case 'medium':
                // 2 columns, medium detail
                this.container.style.gridTemplateColumns = 'repeat(2, 1fr)';
                break;
            case 'small':
                // 1 column, compact
                this.container.style.gridTemplateColumns = '1fr';
                break;
            case 'xsmall':
                // Single column, minimal
                this.container.style.gridTemplateColumns = '1fr';
                this.container.style.fontSize = '12px';
                break;
        }
    }

    destroy() {
        this.observer.disconnect();
    }
}

// Usage
const grid = new ResponsiveGrid(document.querySelector('.product-grid'));

Expected output: As the container width changes (window resize, sidebar toggle, etc.), the grid switches between layouts. The class and grid template update accordingly.

Auto-Growing Textarea

Make a textarea grow as the user types.

const textarea = document.querySelector('textarea.auto-grow');

const textareaObserver = new ResizeObserver(() => {
    // This observer watches the textarea
    console.log('Textarea resized');
});

textareaObserver.observe(textarea);

// However, directly observing a textarea for auto-grow
// does not work because its height changes cause the observer
// to fire, which could cause loops.

// Better approach: use input event to set height
textarea.addEventListener('input', function() {
    // Reset height to calculate scrollHeight correctly
    this.style.height = 'auto';
    // Set to scrollHeight (the actual content height)
    this.style.height = this.scrollHeight + 'px';

    console.log('Textarea resized to:', this.scrollHeight);
});

// Or use ResizeObserver on the parent container
// and adjust child content based on container size
const parentContainer = textarea.parentElement;
const layoutObserver = new ResizeObserver((entries) => {
    const width = entries[0].contentRect.width;
    textarea.style.maxWidth = width < 400 ? '100%' : '60%';
});
layoutObserver.observe(parentContainer);

Expected output: The textarea grows vertically as the user types more content. The parent container's resize observer adjusts the textarea's max-width.

Chart or Canvas Resizing

Keep canvases and charts sharp when containers resize.

const chartContainer = document.querySelector('.chart-container');
const canvas = document.querySelector('#myChart');

const chartObserver = new ResizeObserver(function(entries) {
    const entry = entries[0];
    const width = Math.floor(entry.contentRect.width);
    const height = Math.floor(entry.contentRect.height);

    console.log(`Resizing canvas to ${width}x${height}`);

    // Set canvas size (accounts for device pixel ratio)
    const dpr = window.devicePixelRatio || 1;
    canvas.width = width * dpr;
    canvas.height = height * dpr;
    canvas.style.width = width + 'px';
    canvas.style.height = height + 'px';

    // Redraw chart content (scaled by dpr)
    const ctx = canvas.getContext('2d');
    ctx.scale(dpr, dpr);

    // Redraw (implementation depends on chart library)
    redrawChart(ctx, width, height);
});

chartObserver.observe(chartContainer);

function redrawChart(ctx, w, h) {
    // Clear
    ctx.clearRect(0, 0, w, h);

    // Draw chart
    ctx.fillStyle = '#3498db';
    const barWidth = w / 12;
    const data = [40, 65, 30, 85, 55, 70, 45, 90, 35, 75, 60, 80];

    data.forEach((value, i) => {
        const barHeight = (value / 100) * h * 0.8;
        ctx.fillRect(
            i * barWidth + 5,
            h - barHeight - 20,
            barWidth - 10,
            barHeight
        );
    });
}

// ResizeObserver automatically handles window resize,
// panel expand/collapse, and dynamic layout changes

Expected output: When the chart container changes size (window resize, sidebar toggle), the canvas dimensions update and the chart redraws at the correct resolution.

Common Mistakes

  1. Creating infinite resize loops — If the observer callback changes the element's size, it triggers another observation. Use breakpoints with thresholds to prevent oscillation.
  2. Forgetting to unobserve on component unmount — In frameworks, disconnect the observer in the cleanup/destroy lifecycle to prevent memory leaks.
  3. Using contentRect.width instead of contentBoxSize — contentRect includes padding but has known rounding issues. contentBoxSize is more accurate for modern browsers.
  4. Observing the wrong element — If you observe an element with fixed dimensions, the callback never fires. Observe the parent container or the element that actually changes size.
  5. Ignoring devicePixelRatio for canvas — Without DPR scaling, canvases look blurry on Retina displays. Multiply canvas dimensions by window.devicePixelRatio.

Practice Questions

  1. What is the difference between ResizeObserver and the window resize event? window.resize fires when the viewport changes. ResizeObserver fires when a specific element changes size, regardless of the cause.
  2. What are contentBoxSize and borderBoxSize? They report the element's size in the respective CSS box models. contentBoxSize is the content area. borderBoxSize includes padding and border.
  3. How do you prevent infinite resize loops? Use layout breakpoints that only trigger action when crossing thresholds. Do not resize the observed element inside the callback.
  4. Challenge: Build a responsive dashboard with four panels. Each panel contains a chart. When the browser window is resized, the panels should reflow from 2x2 grid to 1x4 stack at a 600px container width. Use ResizeObserver on the dashboard container.

FAQ

Is ResizeObserver supported in all browsers?

It is supported in all modern browsers. For very old browsers, use a polyfill or fall back to window resize events.

Does ResizeObserver fire on initial page load?

Yes. The callback fires once when observation begins, reporting the element's initial size.

Can I observe the document's root element?

Yes. observer.observe(document.documentElement) watches the entire document, but window.resize may be more appropriate.

What triggers ResizeObserver?

Any change in the element's size: CSS changes, window resize, content changes, JavaScript dimension modifications.

How does ResizeObserver handle display:none elements?

Elements with display:none have zero dimensions. The observer fires when the display changes to a visible value.

Mini Project

Build a responsive card component that changes its layout based on available width. At widths above 600px, show an image on the left and content on the right. At 300-600px, stack image on top of content. Below 300px, show a minimal version with just the title and a "Read more" link. Use ResizeObserver on the card container. Apply smooth CSS transitions for layout changes.

What's Next

Continue with Lesson 23: DOM Performance to learn how to optimize DOM operations for smooth 60fps rendering and minimal layout thrashing.

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro