$nextTick
in Vue.jsA frontend developer (let’s say his name is Eric) walks into a Vue bar.
Eric orders his favorite cocktail: the Nuxt. The bartender is working on it. Then he proceeds to rant.
He begins with how he just discovered $nextTick
in the Vue 3 documentation under instance methods and is blown away. Eric has been working with Vue for some time and is used to writing $watch
and $emit
as instance methods. So what’s $nextTick
for? The Vue documentation says that it “[defers] the callback to be executed after the next DOM update cycle.”
But Eric isn’t convinced.
He continues by narrating how he tried something like this:
this.loadingAnimation = true this.startVeryLongCalculation() this.completeVeryLongCalculation() this.loadingAnimation = false
But the browser shows nothing. He heads over to Stack Overflow and someone recommends using $nextTick
. This person says, “It defers the callback,” but at least they include a code snippet. Eric modifies his code to something like this:
this.loadingAnimation = true this.$nextTick(() => this.startVeryLongCalculation()) this.endVeryLongCalculation() this.loadingAnimation = false
It works. Why?
In this article, if you’re in a similar situation to Eric, you’re going to understand how nextTick
works and see proof with a real use case.
You should have basic knowledge of the following. If you don’t, review these topics:
nextTick
do?nextTick
accepts a callback function that gets delayed until the next DOM update cycle. It is just the Vue way of saying, “Hey, if you ever want to execute a function after the DOM has been updated (which happens on very rare occasions), I would prefer you use nextTick
rather than setTimeout
“:
Vue.nextTick(() => {}) // syntax
We will get to that setTimeout
vs. nextTick
argument very shortly. Let’s visualize nextTick
behavior with this example:
<template> <div> {{ currentTime }} </div> </template> <script> export default { name: 'getCurrentTime', data() { return { currentTime: '' } }, mounted() { this.currentTime = 3; this.$nextTick(() => { let date = new Date() this.currentTime = date.getFullYear() }); } } </script>
Note:
this.$nextTick
is the same as the global API methodvue.nextTick
, except that thethis
of the callback function is automatically bound to the instance that calls it.
Run this code snippet on JSFiddle or on your computer. It will display 2021
. It’s not that if you remove nextTick
you won’t get the same result. However, you should understand that Vue makes changes to the DOM based on what’s in the data.
In the code snippet above, Vue updates the DOM to 3
, then invokes the callback, updates the DOM to 2021
, and finally gives control to the browser, which displays 2021
.
So far, we’ve explored the part where nextTick
inserts the callback function in the callback queue and executes the function when it’s appropriate.
That’s all you need to know.
But it will interest you to know that the callback in nextTick
is used as a microtask in the event loop. The source code for nextTick
explicitly states that “the nextTick
behavior leverages the microtask queue, which can be accessed via either native Promise.then
or MutationObserver
.”
setTimeout
vs. nextTick
The other way to execute a function after the DOM has been updated is by using the JavaScript setTimeout()
function.
Let’s replace nextTick
with setTimeout
in the same code example above:
<template> <div> {{ currentTime }} </div> </template> <script> export default { name: 'getCurrentTime', data() { return { currentTime: '' } }, mounted() { this.currentTime = 3; setTimeout(() => { let date = new Date() this.currentTime = date.getFullYear() }, 0); } } </script>
Run this code snippet on your local server or this JSFiddle. You will see 3
first and then 2021
. It happens fast, so you might need to refresh the browser if you don’t see this behavior at first.
In the code snippet above, Vue updates the DOM to 3
and gives the browser control. Then the browser displays 3
, invokes the callback, updates the DOM to 2021
, and finally gives control to the browser, which now displays 2021
.
The nextTick
implementation uses setTimeout
as the last-resort fallback method on browsers (IE 6–10 and Opera Mini browsers) where Promise
and MutationObserver
are not available. It even prefers setImmediate
for browsers (IE 10) that don’t support Promise
and MutationObserver
.
The only browser that doesn’t have all three methods and has to fall back on setTimeout
are Opera Mini browsers.
nextTick
There are very few cases that would warrant you to pull out the big nextTick
guns. Some of those cases are:
setTimeout
Uncaught (in promise) DOMException
while trying to perform asynchronous action. Remember, Vue updates the DOM asynchronouslyLet’s go through one final example using Vue 3:
<div id="app"> <div ref="listScroll" class="scrolledList"> <ul ref="scrolledHeight"> <li v-for="month in months"> {{month}} </li> </ul> </div> <input type="text" placeholder="Add Month" v-model="month"> <button @click="addMessage" @keyup.enter="addMessage"> Add Month</button> </div> <script src="https://unpkg.com/vue@next"> Vue.createApp({ data() { return { month: '', months: ['Jan', 'Feb', 'Apr', 'May', 'June', 'July', 'Aug'] } }, mounted() { this.updateScrollNextTick() }, methods: { addMessage() { if(this.month == ''){ return } this.months.push(this.month) this.month = '' this.updateScrollNextTick() }, updateScrollNextTick () { let scrolledHeight = this.$refs.scrolledHeight.clientHeight this.$nextTick(() => { this.$refs.listScroll.scrollTo({ behavior: 'smooth', top: scrolledHeight }) }) } }, }) .mount("#app") </script>
Run this on your local machine or on CodePen. You should get something like this:
In the code snippet above, we want to get that smooth scroll-down effect when a new item is added to the list. Go through the code and experiment with modifying it by removing nextTick
, and you will lose that smooth scroll effect. You can also try replacing nextTick
with setTimeout
.
In this article, we’ve explored how nextTick
works. We went further to understand how it differs from the vanilla JavaScript setTimeout
and covered practical use cases.
The aforementioned instance method is rarely needed, so if you’ve had to use it recently, please drop a comment under this post and share your experience.
Debugging Vue.js applications can be difficult, especially when there are dozens, if not hundreds of mutations during a user session. If you’re interested in monitoring and tracking Vue mutations for all of your users in production, try LogRocket.
LogRocket is like a DVR for web and mobile apps, recording literally everything that happens in your Vue apps, including network requests, JavaScript errors, performance problems, and much more. Instead of guessing why problems happen, you can aggregate and report on what state your application was in when an issue occurred.
The LogRocket Vuex plugin logs Vuex mutations to the LogRocket console, giving you context around what led to an error and what state the application was in when an issue occurred.
Modernize how you debug your Vue apps — start monitoring for free.
Would you be interested in joining LogRocket's developer community?
Join LogRocket’s Content Advisory Board. You’ll help inform the type of content we create and get access to exclusive meetups, social accreditation, and swag.
Sign up nowThe useReducer React Hook is a good alternative to tools like Redux, Recoil, or MobX.
Node.js v22.5.0 introduced a native SQLite module, which is is similar to what other JavaScript runtimes like Deno and Bun already have.
Understanding and supporting pinch, text, and browser zoom significantly enhances the user experience. Let’s explore a few ways to do so.
Playwright is a popular framework for automating and testing web applications across multiple browsers in JavaScript, Python, Java, and C#. […]