Skip to content
On this page

Best Practice

Destructuring

Most of the functions in VueUse return an object of refs that you can use ES6's object destructure syntax on to take what you need. For example:

ts
import { useMouse } from '@vueuse/core'

// "x" and "y" are refs
const { x, y } = useMouse()

console.log(x.value)

const mouse = useMouse()

console.log(mouse.x.value)
import { useMouse } from '@vueuse/core'

// "x" and "y" are refs
const { x, y } = useMouse()

console.log(x.value)

const mouse = useMouse()

console.log(mouse.x.value)

If you prefer to use them as object properties style, you can unwrap the refs by using reactive(). For example:

ts
import { reactive } from 'vue'
import { useMouse } from '@vueuse/core'

const mouse = reactive(useMouse())

// "x" and "y" will be auto unwrapped, no `.value` needed
console.log(mouse.x)
import { reactive } from 'vue'
import { useMouse } from '@vueuse/core'

const mouse = reactive(useMouse())

// "x" and "y" will be auto unwrapped, no `.value` needed
console.log(mouse.x)

Side-effect Clean Up

Similar to Vue's watch and computed that will be disposed when the component is unmounted, VueUse's functions also clean up the side-effects automatically.

For example, useEventListenerwill call removeEventListener when the component is unmounted so you don't need to worry about it.

ts
// will cleanup automatically
useEventListener('mousemove', () => {})
// will cleanup automatically
useEventListener('mousemove', () => {})

All VueUse functions follow this convention.

To manually dispose the side-effects, some function returns a stop handler just like the watch function. For example:

ts
const stop = useEventListener('mousemove', () => {})

// ...

// unregister the event listener manually
stop()
const stop = useEventListener('mousemove', () => {})

// ...

// unregister the event listener manually
stop()

While not all function return the handler, a more general solution is to use the effectScope API from Vue.

ts
import { effectScope } from 'vue'

const scope = effectScope()

scope.run(() => {
  // ...

  useEventListener('mousemove', () => {})
  onClickOutside(el, () => {})
  watch(source, () => {})
})

// all composables called inside `scope.run` will be disposed
scope.stop()
import { effectScope } from 'vue'

const scope = effectScope()

scope.run(() => {
  // ...

  useEventListener('mousemove', () => {})
  onClickOutside(el, () => {})
  watch(source, () => {})
})

// all composables called inside `scope.run` will be disposed
scope.stop()

You can learn more about effect scope in this RFC.

Passing Ref as Argument

In Vue, we use the setup() function to construct the "connections" between the data and logics. To make it flexible, most of the VueUse function also accpets ref version of the arguments.

Taking useTitleas an example:

Normal usage

Normally useTitlereturn a ref that reflects to the page's title. When you assign new value to the ref, it automatically updates the title.

ts
const isDark = useDark()
const title = useTitle('Set title')

watch(isDark, () => {
  title.value = isDark.value ? '🌙 Good evening!' : '☀️ Good morning!'
})
const isDark = useDark()
const title = useTitle('Set title')

watch(isDark, () => {
  title.value = isDark.value ? '🌙 Good evening!' : '☀️ Good morning!'
})
Connection usage

If you think in "connection", you can instead passing a ref that make it bind to the page's title.

ts
const isDark = useDark()
const title = computed(() => isDark.value ? '🌙 Good evening!' : '☀️ Good morning!')

useTitle(title)
const isDark = useDark()
const title = computed(() => isDark.value ? '🌙 Good evening!' : '☀️ Good morning!')

useTitle(title)
Reactive Getter

Since VueUse 9.0, we introduce a new convention for passing "Reactive Getter" as the argument. Which works great with reactive object and Reactivity Transform.

ts
const isDark = useDark()

useTitle(() => isDark.value ? '🌙 Good evening!' : '☀️ Good morning!')
const isDark = useDark()

useTitle(() => isDark.value ? '🌙 Good evening!' : '☀️ Good morning!')

Released under the MIT License.