VueUse is an open source project by Anthony Fu that provides Vue developers with a large number of basic Composition API utility functions for Vue 2 and Vue 3.

It has dozens of solutions for common developer use cases such as tracking Ref changes, detecting element visibility, simplifying common Vue patterns, keyboard/mouse input, and more. This is a great way to really save development time because you don’t have to add all these standard features yourself.

I like the VueUse library because it really puts the developer first when deciding which utilities to provide, and it’s a well-maintained library because it keeps pace with the current version of Vue.

What are the VueUse utilities?

If you want to see a complete list of each and every utility, I definitely recommend you go to the official documentation. But to summarize, there are nine types of functions in VueUse.

  1. Animation – Contains easy to use transitions, timeouts, and timings.
  2. Browser – Can be used for different screen controls, clipboard, preferences, etc.
  3. Component – Provides shorthand for different Component methods.
  4. Formatters – Provides response time formatting.
  5. Sensors – to listen for different DOM events, input events, and network events
  6. State — Manages user State (global, local, session storage).
  7. Utility – different Utility functions such as getters, conditions, reference synchronization, etc.
  8. Watch – More advanced types of observers, such as pausing observers, retreating observers, and conditional observers.
  9. Misc – Different types of events, WebSockets, and Web Workers functionality

Most of these categories contain several different features, so VueUse is flexible for your usage situation and can be a great place to quickly start building Vue applications.

In this tutorial, we’ll take a look at five different VueUse functions so you can see how easy it is to work in this library.

But first, let’s add it to the Vue project!

Install VueUse into your Vue project

One of the best features of VueUse is that it is compatible with both Vue 2 and Vue 3 using only one package!

There are two options for installing VueUse: NPM or CDN

npm i @vueuse/core # yarn add @vueuse/core
Copy the code
<script src="https://unpkg.com/@vueuse/shared"></script>
<script src="https://unpkg.com/@vueuse/core"></script>
Copy the code

I recommend using NPM because its usage is easier to understand, but if we use CDN, VueUse will be accessible in the application via window.vueuse.

For the NPM installation, all functionality can be accessed by importing from @vueuse/core using standard object refactoring, like this.

import { useRefHistory } from '@vueuse/core'
Copy the code

Ok, now that we have VueUse installed, let’s use it in our application!

UseRefHistory tracks changes to reactive data

UseRefHistory keeps track of every change made to Ref and stores it in an array. This enables us to easily provide undo and redo capabilities for our applications.

Let’s look at an example where we’re building a text area that we want to undo.

The first step is to create our base component — using refs, textareas, and buttons for undo and redo — without using VueUse.

<template>
  <p> 
    <button> Undo </button>
    <button> Redo </button>
  </p>
  <textarea v-model="text"/>
</template>

<script setup>
import { ref } from 'vue'
const text = ref(' ')
</script>

<style scoped>
  button {
    border: none;
    outline: none;
    margin-right: 10px;
    background-color: #2ecc71;
    color: white;
    padding: 5px 10px;;
  }
</style>
Copy the code

Then, let’s add VueUse by importing the useRefHistory function and extracting the history, undo, and redo attributes from our text ref. This is as simple as calling useRefHistory and passing our ref.

import { ref } from 'vue'
import { useRefHistory } from '@vueuse/core'

const text = ref(' ')
const { history, undo, redo } = useRefHistory(text)
Copy the code

Every time our REF changes, this triggers an observer — updating the history property we just created.

Then, to really see what’s happening, let’s print out the history in the template and call our undo and redo functions when we click the appropriate button.

<template>
  <p> 
    <button @click="undo"> Undo </button>
    <button @click="redo"> Redo </button>
  </p>
  <textarea v-model="text"/>
  <ul>
    <li v-for="entry in history" :key="entry.timestamp">
      {{ entry }}
    </li>
  </ul>
</template>

<script setup>
import { ref } from 'vue'
import { useRefHistory } from '@vueuse/core'
const text = ref(' ')
const { history, undo, redo } = useRefHistory(text)
</script>

<style scoped>
  button {
    border: none;
    outline: none;
    margin-right: 10px;
    background-color: #2ecc71;
    color: white;
    padding: 5px 10px;;
  }
</style>
Copy the code

Ok, so let’s run it. As we type, each character triggers a new entry in the history array, and if we click undo/redo, we go to the corresponding entry.

There are different options to add more functionality to this feature. For example, we can drill down to reflect objects and limit the number of such historical entries.

const { history, undo, redo } = useRefHistory(text, {
  deep: true.capacity: 10,})Copy the code

Be sure to review the documentation for a complete list of options.

OnClickOutside closes the mode

OnClickOutside detects any clicks outside an element. In my experience, the most common use of this feature is to close any mode or pop-up window.

Often, we want our modes to block the rest of the page to catch the user’s attention and limit errors. However, if they do click on something outside of the mode, we want it to be turned off.

This can be done in just two steps:

  1. Create a template reference for the element we want to examine
  2. Use this template reference to run onClickOutside

This is a simple component with a pop-up window that uses onClickOutside.

<template>
  <button @click="open = true"> Open Popup </button>
  <div class="popup" v-if='open'>
    <div class="popup-content" ref="popup">
      Lorem ipsum dolor sit amet consectetur adipisicing elit. Corporis aliquid autem reiciendis eius accusamus sequi, ipsam corrupti vel laboriosam necessitatibus sit natus vero sint ullam! Omnis commodi eos accusantium illum?
    </div>
  </div>
</template>

<script setup>
import { ref } from 'vue'
import { onClickOutside } from '@vueuse/core'
const open = ref(false) // state of our popup
const popup = ref() // template ref
// whenever our popup exists, and we click anything BUT it
onClickOutside(popup, () = > {
  open.value  = false
})
</script>

<style scoped>
  button {
    border: none;
    outline: none;
    margin-right: 10px;
    background-color: #2ecc71;
    color: white;
    padding: 5px 10px;;
  }
  .popup {
    position: fixed;
    top:;left:;width: 100vw;
    height: 100vh;
    display: flex;
    align-items: center;
    justify-content: center;
    background: rgba(,,,0.1);
  }
  .popup-content {
    min-width: 300px;
    padding: 20px;
    width: 30%;
    background: #fff;
  }
</style>
Copy the code

As a result, we can open the popover with our button, and then close it by clicking outside the popover content window.

UseVModel simplifies v-Model binding

A common use case for Vue developers is to create a custom V-Model binding for a component. This means that our component accepts a value as prop, and whenever that value is changed, our component issues an update event to the parent.

The useVModel function reduces this to using only the standard ref syntax. Suppose we have a custom text input and try to create a V-Model for the value of its text input. Normally, we must accept a prop value and emit a change event to update the data value in the parent component.

Instead of using ref and calling props. Value and update:value, we can use useVModel as if it were a normal ref. This helps reduce the number of different grammars we need to remember!

<template>
  <div>
    <input 
           type="text" 
           :value="data"
           @input="update"
           />
  </div>
</template>

<script>
import { useVModel } from '@vueuse/core'
export default {
  props: ['data'].setup(props, { emit }) {
    const data = useVModel(props, 'data', emit)
    console.log(data.value) // equal to props.data
    data.value = 'name' // equal to emit('update:data', 'name')
    const update = (event) = > {
        data.value = event.target.value
    }
    return {
        data,
        update
    }
  },
}
</script>
Copy the code

Whenever we need to access our value, we just call.value, and useVModel will give us the value from our component props. Whenever we change the value of the object, the useVModel issues an update event to the parent component.

Here’s a quick example of what this parent component might look like…

<template>
  <div>
    <p> {{ data }} </p>
    <custom-input 
      :data="data" 
      @update:data="data = $event"
    />
  </div>
</template>

<script>
import CustomInput from './components/CustomInput.vue'
import { ref } from 'vue'
export default {
  components: {
    CustomInput,
  },
  setup () {
    const data = ref('hello')
    return {
      data
    }
  }
}
Copy the code

The result looks like this, where our values in the parent are always synchronized with the input in the child.

Element visibility is tracked using IntersectionObserver

Intersection Observers are powerful when determining whether two elements overlap. A good use case is to check whether the element is currently visible in the viewport.

Essentially, it checks the percentage of the target element that intersects the root element/document. If the percentage exceeds a certain threshold, it calls a callback to determine whether the target element is visible.

UseIntersectionObserver provides a simple syntax for using the IntersectionObserver API. All we need to do is provide a template ref for the element we want to check. By default, IntersectionObserver is based on the viewport of the document, with a threshold of 0.1 — so when this threshold is crossed in any direction, our IntersectionObserver will be triggered.

The code for this example might look like this: We have a false paragraph that just takes up space in our viewport, our target element, and then a print statement that prints the visibility of our element.

<template>
  <p> Is target visible? {{ targetIsVisible }} </p>
  <div class="container">
    <div class="target" ref="target">
      <h1>Hello world</h1>
    </div>
  </div>
</template>

<script>
import { ref } from 'vue'
import { useIntersectionObserver } from '@vueuse/core'
export default {
  setup() {
    const target = ref(null)
    const targetIsVisible = ref(false)
    const { stop } = useIntersectionObserver(
      target,
      ([{ isIntersecting }], observerElement) = > {
        targetIsVisible.value = isIntersecting
      },
    )
    return {
      target,
      targetIsVisible,
    }
  },
}
</script>

<style scoped>
.container {
  width: 80%;
  margin:  auto;
  background-color: #fafafa;
  max-height: 300px;
  overflow: scroll;
}
.target {
  margin-top: 500px;
  background-color: #1abc9c;
  color: white;
  padding: 20px;
}
</style>
Copy the code

When we run it and scroll through it, we’ll see that it’s updated correctly.

You can also specify more options for the Intersection Observer, such as changing its root element, margins (used to calculate the offset of the root bounding box at the Intersection), and threshold levels.

const { stop } = useIntersectionObserver(
  target,
  ([{ isIntersecting }], observerElement) = > {
    targetIsVisible.value = isIntersecting
  },
  {
    // root, rootMargin, threshold, window
    // full options in the source: https://github.com/vueuse/vueuse/blob/main/packages/core/useIntersectionObserver/index.ts
    threshold: 0.5,})Copy the code

Just as importantly, this method returns a stop function, which we can call to stop looking at the intersection. This is especially useful if we only want to track the first time an element is visible on the screen.

In this code, once targetIsVisible is set to true, the observer will stop and our value will remain true even if we scroll away from the target element.

const { stop } = useIntersectionObserver(
  target,
  ([{ isIntersecting }], observerElement) = > {
    targetIsVisible.value = isIntersecting
    if (isIntersecting) {
      stop()
    }
  },
)
Copy the code

UseTransition transitions between values

UseTransition is one of my favorite functions in the entire Veuse library. It allows us to convert values smoothly within a single line.

We have a digital source stored as ref and an output that will be moderated between different values. For example, suppose we want to set up a counter

We can do this in three steps:

  • Create ourcountRef and initialize it to zero
  • useuseTransitioncreateoutputRef (set duration and conversion type)
  • To change thecountThe value of the
<script setup>
import { ref } from 'vue'
import { useTransition, TransitionPresets } from '@vueuse/core'

const source = ref(0)

const output = useTransition(source, {
  duration: 3000.transition: TransitionPresets.easeOutExpo,
})

source.value = 5000

</script>
Copy the code

Then, in our template, we want to display the value of the output, because it transitions smoothly between different values.

<template>
  <h2> 
    <p> Join over </p>
    <p> {{ Math.round(output) }}+ </p>
    <p>Developers </p>
  </h2>
</template>

<script setup>
import { ref } from 'vue'
import { useTransition, TransitionPresets } from '@vueuse/core'
const source = ref()
const output = useTransition(source, {
  duration: 3000.transition: TransitionPresets.easeOutExpo,
})
source.value = 5000
</script>
Copy the code

This is the result!

We can also use useTransition to transition an entire array of numbers, which is useful when dealing with positions or colors. One trick with colors is to use a computed attribute to format the RGB values into the correct color syntax.

<template>
  <h2 :style="{ color: color } "> COLOR CHANGING </h2>
</template>

<script setup>
import { ref, computed } from 'vue'
import { useTransition, TransitionPresets } from '@vueuse/core'
const source = ref([, , ])
const output = useTransition(source, {
  duration: 3000.transition: TransitionPresets.easeOutExpo,
})
const color = computed(() = > {
  const [r, g, b] = output.value
  return `rgb(${r}.${g}.${b}) `
})
source.value = [255.255]
</script>
Copy the code

Some cool ways to further customize are to use any of the built-in transition presets or use CSS easing functions to define our own transitions.

Final thoughts

This is by no means a complete guide to VueUse, these are just a few of the functions I find most interesting in the VueUse library.

I love how all of these utility features help speed up development, because each of them is designed to solve specific but common use cases.


Original article: Learnvue.co, by Matt Maribojoc