effectScope — Group & Dispose Effects

Manage Many Watchers Together

effectScope — Group & Dispose Effects

effectScope groups computed and watchers so you can dispose them all at once — the building block libraries like Pinia and VueUse use internally.

5 min read Level 3/5 #vue#advanced#effects
What you'll learn
  • Create an effectScope
  • Run watchers inside it
  • Dispose with scope.stop()

Vue automatically cleans up watchers and computed values declared inside a component when the component unmounts. But what about effects created outside any component — in a global store, a singleton, or a long-lived composable? effectScope is the answer.

Creating a Scope

import { effectScope, ref, watch, computed } from 'vue'

const scope = effectScope()

scope.run(() => {
  const count = ref(0)
  const doubled = computed(() => count.value * 2)

  watch(count, (v) => console.log('count is now', v))
  setInterval(() => count.value++, 1000)
})

// Later — dispose everything in one call
scope.stop()

scope.stop() halts every watch, watchEffect, and computed created inside scope.run().

Detached Scopes

By default a scope inherits the active parent scope. Pass true to detach — useful when you want a scope that survives its parent.

const detached = effectScope(true)
detached.run(() => { /* lives independent of parent */ })

In a Composable

A composable that owns many effects can return a stop function for callers.

import { effectScope } from 'vue'

export function useFeed() {
  const scope = effectScope()
  scope.run(() => {
    watch(/* ... */)
    watch(/* ... */)
  })
  return { stop: () => scope.stop() }
}

Where You’ll See This

You usually don’t reach for effectScope directly — Pinia uses it to dispose store effects on HMR, and VueUse uses it to give composables stop() controls. Knowing it exists helps when you read library source.

toRef, toRefs & unref →