useMutationObserver
A composable for detecting DOM changes using the Mutation Observer API with automatic cleanup.
Usage
The useMutationObserver composable wraps the Mutation Observer API to detect changes to the DOM tree. It’s useful for monitoring attribute changes, child element modifications, and character data updates.
Why wrap MutationObserver? The native MutationObserver has no awareness of Vue’s effectScope lifecycle. If you create one inside a composable, it won’t automatically disconnect when the scope is disposed. useMutationObserver integrates onScopeDispose for automatic cleanup, defers creation until after hydration for SSR safety, and adds reactive target tracking — things the native API can’t do on its own.
<script setup lang="ts">
import { useMutationObserver } from '@vuetify/v0'
import { ref, useTemplateRef } from 'vue'
const target = useTemplateRef('target')
const mutationCount = ref(0)
useMutationObserver(target, (mutations) => {
mutationCount.value += mutations.length
mutations.forEach(mutation => {
console.log('Type:', mutation.type)
console.log('Added nodes:', mutation.addedNodes)
console.log('Removed nodes:', mutation.removedNodes)
})
}, {
childList: true,
attributes: true,
attributeOldValue: true
})
</script>
<template>
<div>
<div ref="target">
<p>Mutations detected: {{ mutationCount }}</p>
</div>
</div>
</template>Architecture
useMutationObserver wraps the native MutationObserver API with Vue reactivity:
Options
| Option | Type | Default | Notes |
|---|---|---|---|
immediate | boolean | false | Fire the callback immediately on mount before any mutation |
once | boolean | false | Stop observing after the first callback fires |
childList | boolean | true | Observe child node additions and removals |
attributes | boolean | false | Observe attribute changes |
characterData | boolean | false | Observe text content changes |
subtree | boolean | false | Extend observation to all descendant nodes |
attributeFilter | string[] | — | Limit attribute observation to specific attribute names |
characterDataOldValue | boolean | false | Record previous text value in mutation records |
Reactivity
| Property/Method | Reactive | Notes |
|---|---|---|
isActive | Computed from observer ref | |
isPaused | ShallowRef, readonly | |
target | Accepts MaybeRef, watched for changes | |
pause() | — | Temporarily stop observing without disconnecting |
resume() | — | Resume after pause() |
stop() | — | Disconnect the observer permanently |
Examples
FAQ
pause() stops observation outright; changes during the pause are dropped and are not replayed when you call resume(). Use it to skip noise during bulk DOM writes, then resume once the work is done.
Set subtree: true to extend observation to every descendant, then enable the record types you need — characterData for text, attributes for attribute changes. childList is on by default.
Use useMutationObserver for DOM-tree, attribute, and text changes. Reach for useResizeObserver for element size changes and useIntersectionObserver for viewport visibility.
Yes. Set attributes: true and pass attributeFilter: ['class', 'data-state'] to limit observation to those names, which avoids callback noise from unrelated attribute writes.