← Back to all hooks

useMutationObserver

Observe DOM mutations like added/removed nodes, attribute changes, and text modifications using the MutationObserver API.

Approach 1: Single Element

Observe one element for DOM changes. Detects child additions/removals and attribute changes.

Add/remove items or change attributes to see mutations detected

Observed Container

Item 1
Item 2

Mutation Log (last 10)

No mutations detected yet

Approach 2: Multiple Elements

One observer tracking multiple elements. More efficient for monitoring many containers.

Single Observer tracking multiple containers

Add or remove items from any box

Box 1

AB

Box 2

XY

Box 3

12

Mutation Log (last 10)

No mutations detected yet

Code Examples

Single Element

import { useMutationObserver } from "@uiblock/hooks";

export default function Demo() {
  const ref = useMutationObserver(
    (mutations) => {
      mutations.forEach((mutation) => {
        if (mutation.type === 'childList') {
          console.log('Children changed!');
        }
        if (mutation.type === 'attributes') {
          console.log('Attribute changed:', mutation.attributeName);
        }
      });
    },
    {
      childList: true,
      attributes: true,
      subtree: true
    }
  );

  return <div ref={ref}>Observed content</div>;
}

Multiple Elements

import { useMutationObserverMultiple } from "@uiblock/hooks";
import { useRef, useEffect } from "react";

export default function Demo() {
  const { observe } = useMutationObserverMultiple(
    (mutations) => {
      mutations.forEach((mutation) => {
        const id = mutation.target.getAttribute('data-mutation-id');
        console.log(`Mutation in ${id}`);
      });
    },
    { childList: true }
  );

  const box1Ref = useRef(null);
  const box2Ref = useRef(null);

  useEffect(() => {
    if (box1Ref.current) observe(box1Ref.current, 'box1');
    if (box2Ref.current) observe(box2Ref.current, 'box2');
  }, [observe]);

  return (
    <div>
      <div ref={box1Ref}>Box 1</div>
      <div ref={box2Ref}>Box 2</div>
    </div>
  );
}

Mutation Types

childList: true

Observe additions and removals of child nodes

attributes: true

Observe changes to element attributes (class, style, data-*, etc.)

characterData: true

Observe changes to text content

subtree: true

Observe all descendants, not just direct children

attributeOldValue: true

Record the previous value of changed attributes

How It Works

Here's the implementation:

import { useEffect, useRef } from 'react'

export function useMutationObserver(callback, options?) {
  const ref = useRef(null)
  const callbackRef = useRef(callback)

  useEffect(() => {
    callbackRef.current = callback
  }, [callback])

  useEffect(() => {
    const element = ref.current
    if (!element) return

    const observer = new MutationObserver((mutations, observer) => {
      callbackRef.current(mutations, observer)
    })

    observer.observe(element, options)

    return () => observer.disconnect()
  }, [options])

  return ref
}

Multiple Elements Version

For observing multiple elements with a single observer:

import { useEffect, useRef, useCallback } from 'react'

export function useMutationObserverMultiple(callback, options?) {
  const observerRef = useRef(null)
  const callbackRef = useRef(callback)

  useEffect(() => {
    callbackRef.current = callback
  }, [callback])

  useEffect(() => {
    observerRef.current = new MutationObserver((mutations, observer) => {
      callbackRef.current(mutations, observer)
    })

    return () => observerRef.current?.disconnect()
  }, [])

  const observe = useCallback((element, id) => {
    if (!element || !observerRef.current) return

    element.setAttribute('data-mutation-id', id)
    observerRef.current.observe(element, options)
  }, [options])

  return { observe }
}

API Reference

useMutationObserver

Parameters

callback: MutationCallback

Function called when mutations occur

options?: MutationObserverInit

Configuration (childList, attributes, characterData, subtree, etc.)

Returns

ref - Ref to attach to the element to observe

useMutationObserverMultiple

Parameters

callback: MutationCallback

Function called when mutations occur

options?: MutationObserverInit

Configuration options

Returns

observe(element, id) - Function to observe an element with a unique ID

💡 Use Cases

  • Detecting when content is dynamically added/removed
  • Monitoring third-party widget changes
  • Tracking attribute changes for analytics
  • Syncing UI state with DOM changes
  • Building dev tools and debugging utilities
  • Implementing undo/redo functionality