# MutationObserver and ResizeObserver

> Use MutationObserver to watch DOM changes and ResizeObserver to track element size changes, with practical examples and cleanup via disconnect().

Author: [Flavio Copes](https://flaviocopes.com/about/) | Published: 2026-08-19 | Topics: [Web Platform](https://flaviocopes.com/tags/platform/) | Canonical: https://flaviocopes.com/mutationobserver-resizeobserver/

Browsers give you two observer APIs for reacting to page changes. **MutationObserver** watches the [DOM](https://flaviocopes.com/dom/) tree, attributes, and text. **ResizeObserver** watches element size.

They replace older hacks like polling or listening to `window` resize for every layout tweak.

## MutationObserver

A MutationObserver runs a callback when nodes are added, removed, or changed.

You pass a target element and an options object. Common flags are `childList`, `attributes`, and `subtree`.

```js
const list = document.querySelector('#comments')

const observer = new MutationObserver((mutations) => {
  mutations.forEach((mutation) => {
    mutation.addedNodes.forEach((node) => {
      if (node.nodeType !== Node.ELEMENT_NODE) return
      console.log('New comment added:', node.textContent)
    })
  })
})

observer.observe(list, {
  childList: true,
  subtree: true
})
```

This fires when someone appends a new comment node inside `#comments`.

Set `attributes: true` if you care about class or style changes on the target. Add `attributeFilter: ['class']` to limit noise.

## A practical example: detecting added nodes

Say a chat app injects messages into a container. You want to scroll to the bottom on each new message.

```js
const chat = document.querySelector('#chat')

const observer = new MutationObserver(() => {
  chat.scrollTop = chat.scrollHeight
})

observer.observe(chat, { childList: true })
```

No need to hook into every function that adds a message. The observer catches all of them.

## ResizeObserver

**ResizeObserver** fires when an element's content box changes size. It is more precise than a window resize [event](https://flaviocopes.com/javascript-events/).

```js
const sidebar = document.querySelector('#sidebar')

const observer = new ResizeObserver((entries) => {
  entries.forEach((entry) => {
    const { width, height } = entry.contentRect
    console.log(`Sidebar is ${width}x${height}`)
  })
})

observer.observe(sidebar)
```

Use this when a chart, canvas, or sticky panel must reflow after its container shrinks or grows.

A `window` resize listener misses cases where only one panel changes, like a collapsible sidebar. ResizeObserver catches those.

## When to use each

| Observer | Watches |
|----------|---------|
| MutationObserver | DOM tree changes, attribute changes |
| ResizeObserver | Element width and height |

Use MutationObserver for dynamic lists, third-party widgets that inject HTML, or syncing state when the [DOM](https://flaviocopes.com/dom/) changes.

Use ResizeObserver for charts, maps, custom scroll areas, and any layout that depends on element dimensions.

## Cleanup with disconnect()

Both observers keep watching until you stop them. Call `disconnect()` when your component unmounts or no longer needs updates.

```js
mutationObserver.disconnect()
resizeObserver.disconnect()
```

My advice is to store the observer in a variable and disconnect it in the matching cleanup code. An observer you no longer need can keep callbacks and referenced state alive.
