Vue's nextTick Waits for the Update Queue, Not the Render: Five Pitfalls That Break Your Charts
Mistaking nextTick for a render callback silently breaks chart sizing, scroll math, and SSR guards. The fix is to treat nextTick as a microtask boundary and let rendering-aware APIs like ResizeObserver and flush:'post' handle the rest.
Vue batches component updates into a single queue and flushes it in a microtask. nextTick schedules a callback after that flush, so it guarantees the virtual DOM is now the real DOM. It gives no guarantee about style computation, layout, or paint; a container can exist with offsetWidth 0. That's why the classic modal-with-a-chart pattern fails: the DOM is in place, but the browser hasn't run layout when nextTick fires.
The same misunderstanding crops up in five common scenarios. Initializing charts after a modal or tab switch needs a requestAnimationFrame wrapper or a ResizeObserver subscriber. Calculating scroll height after list updates requires waiting on image load events and document.fonts.ready. Async child components don't respond to a parent's nextTick; they need Suspense or an explicit emit from onMounted. Mutating reactive data inside a nextTick callback can trigger an endless update loop, and SSR environments have no DOM at all, so DOM code must live in onMounted. Tests also need flushPromises to drain the update queue.
Vue 3.5's declarative alternatives eliminate most manual timing: useTemplateRef for typed refs, watch with flush:'post' to run callbacks after DOM updates, Suspense for async children, and ResizeObserver for size-aware logic. nextTick remains a microtask boundary; it will never wait for browser rendering, and Vue 3.6's Vapor mode won't change that.
nextTick's guarantee is deliberately narrow; the confusion comes from treating it as a universal 'after DOM' hook when it only covers Vue's own patching, not the browser's rendering pipeline.
Because browser layout and paint happen after microtasks, any measurement of element geometry right after nextTick is a lottery. Using ResizeObserver shifts the responsibility to the browser, which is the only thing that knows the real dimensions.
Vue's direction toward declarative scheduling (watch with flush:'post', Suspense) suggests that imperative waiting is a code smell; most nextTick uses can be expressed as reactive dependencies.