Back

nextTick in Vue 3

nextTick in Vue 3

article nextTick poster

Changing Vue component data (props or state) does not immediately change the DOM. Rather, Vue updates the DOM asynchronously. You can catch the moment when Vue updates the DOM using the nextTick() function.

Flow

The nextTick occurs after the state has been updated, i.e., some show flag has been changed and is prepared to be presented in the DOM, but before the re-render - reloading the UI. It is not yet visible but has already been updated.

graph.png

nextTick()

As component data changes, the DOM is updated asynchronously.

Additional Info

Vue collects multiple updates to the virtual DOM from all components and then creates a single batch of changes to update the DOM with them. Updating the DOM in a single batch is more efficient than performing many small updates.

For example, consider a component that toggles the display of an element:

<script setup>
import { ref } from 'vue'

const show = ref(true)
const content = ref()

const handleClick = () => {
  show.value = !show.value
  console.log(show.value, content.value)
}
</script>
<template>
  <div>
    <button @click="handleClick">Insert/Remove</button>
    <div v-if="show" ref="content">I am an element</div>
  </div>
</template>

Clicking the "Insert/Remove" button changes the show flag, which toggles the display of the <div id="content"> element using the v-if="show" directive.

Looking at handleClick, right after mutating the show.value = !show.value data, the implemented DOM data does not match the show value. If show is true, then content is null, which means that the DOM is not synchronized with the component data.

If you want to catch the moment when the DOM has just been updated, you need to use the special function nextTick(callback). It performs a callback after new data reaches the DOM.

Let's find the moment when a <div> element is inserted or removed from the DOM:

<script setup>
import { ref, nextTick } from 'vue'
const show = ref(true)
const content = ref()
const handleClick = () => {
  show.value = !show.value
  // ---------------
  nextTick(() => {
    console.log(show.value, content.value)
  })
  // ---------------
}
</script>
<template>
  <div>
    <button @click="handleClick">Insert/Remove</button>
    <div v-if="show" ref="content">I am an element</div>
  </div>
</template>

You can see that content (a reference that contains the <div> element) is null or contains an element that exactly matches the show value.

In addition, nextTick(callback) performs a callback when all updates to the child components have been passed to the DOM.

nextTick() with async/await

If nextTick() is called without arguments:

<script setup>
import { ref, nextTick } from 'vue'
const show = ref(true)
const content = ref()
const handleClick = async () => {
  show.value = !show.value
    // ---------
  await nextTick()
    // ---------
  console.log(show.value, content.value)
}
</script>
<template>
  <div>
    <button @click="handleClick">Insert/Remove</button>
    <div v-if="show" ref="content">I am an element</div>
  </div>
</template>

will return a promise that resolves when the component data reaches the DOM - it will be updated but over-rendered.

This helps take advantage of the more readable async/await syntax.

For example, we make the previous component more readable by capturing the DOM update using the async/await syntax:

const handleClick = async () => {...}

has been marked as an asynchronous function. When the Insert/Remove button is clicked, the show value changes.

The await nextTick() waits until the changes reach the DOM update. Finally,

console.log(content)

logs the actual contents of the ref.

Summary

When we change the component data, Vue updates the DOM asynchronously. If we want to catch the moment when the DOM has been updated after the component data has been changed - but not yet redrawn, then we need to use the nextTick(callback) function.

Their single argument which is callback is called immediately after the DOM update and we are guaranteed to get the next DOM in sync with the component data.

Alternatively, if you don't pass the callback argument to nextTick(): functions will return a promise that will be resolved when the DOM is updated.