Top 60 Vue Interview Questions (2026)

The 60 Vue questions interviewers actually ask, with direct answers, runnable code, and what the interviewer is listening for. Grouped for freshers, intermediate, and experienced rounds.

60 questions with answers

What Is Vue?

Key Takeaways

  • Vue is a progressive JavaScript framework for building user interfaces, with a reactive data model and a component-based structure.
  • It ships two API styles: the Options API (organized by option type) and the Composition API (organized by logical concern with setup and composables).
  • Interviews test reactivity (ref, reactive, computed), component communication (props, emits, slots), and lifecycle, not just template syntax.
  • This page is a question bank: work your tier first, read one tier up, and say answers out loud because delivery is evaluated too.

Vue is a progressive JavaScript framework for building user interfaces, created by Evan You and first released in 2014. Progressive means you can adopt it incrementally: drop it into one page with a script tag, or build a full single-page application with routing, state management, and a build step. Its core idea is reactivity: you declare data, bind it in a template, and Vue updates the DOM when that data changes. The official Vue documentation describes it as building on standard HTML, CSS, and JavaScript, which is why the template syntax feels close to plain markup. In interviews, Vue questions probe how reactivity actually works (ref versus reactive, computed versus watch), how components talk to each other (props down, events up, slots, provide/inject), and the Vue 3 shift toward the Composition API. This page collects the 60 questions that come up most, each with a direct answer and runnable code. Increasingly the first frontend round is a live AI coding interview, so pair this bank with our AI interview preparation guides for the format side.

60Questions with answers on this page
3Difficulty tiers: freshers, intermediate, experienced
Vue 3Version these answers assume, Composition API included
45-60 minTypical length of a Vue technical round

Watch: Vue.js Explained in 100 Seconds

Video: Vue.js Explained in 100 Seconds (Fireship, YouTube)

Test yourself and earn a certificate

10 quick questions. Score 70%+ to download your Vue certificate.

Jump to quiz

All Questions on This Page

60 questions
Vue Interview Questions for Freshers
  1. 1. What is Vue and what makes it progressive?
  2. 2. What is reactivity in Vue, and how do you declare reactive state?
  3. 3. What is the difference between ref and reactive?
  4. 4. What is a computed property, and how does it differ from a method?
  5. 5. When do you use computed versus watch?
  6. 6. What are directives in Vue, and can you name the common ones?
  7. 7. What is the difference between v-if and v-show?
  8. 8. How does v-for work, and why does it need a key?
  9. 9. What does v-model do, and what is it sugar for?
  10. 10. What are props, and what are the rules for using them?
  11. 11. How does a child component communicate back to its parent?
  12. 12. What are the main lifecycle hooks in Vue 3?
  13. 13. What is a Single-File Component (SFC)?
  14. 14. How does template interpolation and attribute binding work?
  15. 15. How do you handle events, and what are event modifiers?
  16. 16. How do you bind classes and styles dynamically?
  17. 17. What is the difference between the Options API and the Composition API?
  18. 18. Why should you avoid using v-if and v-for on the same element?
  19. 19. How do you scaffold a new Vue project today?
  20. 20. Is Vue's data flow one-way or two-way?
  21. 21. Can a computed property be writable?
Vue Intermediate Interview Questions
  1. 22. How does script setup work, and what does it expose?
  2. 23. What is a composable, and how do you write one?
  3. 24. What is the difference between watch and watchEffect?
  4. 25. What are slots, and how do named and scoped slots differ?
  5. 26. How do provide and inject work, and when should you use them?
  6. 27. What reactivity limitations should you know about?
  7. 28. How does Vue Router handle navigation and dynamic routes?
  8. 29. What is Pinia, and how does a store work?
  9. 30. How does Pinia compare to Vuex?
  10. 31. What are dynamic and async components?
  11. 32. What does the KeepAlive component do?
  12. 33. What is the Teleport component used for?
  13. 34. When would you write a custom directive?
  14. 35. How do you approach form validation in Vue?
  15. 36. How do transitions and animations work in Vue?
  16. 37. How do you implement v-model on a custom component?
  17. 38. What are template refs, and how do you access a DOM element?
  18. 39. What is nextTick, and when do you need it?
  19. 40. What is the difference between global and local component registration?
  20. 41. How do you handle errors from child components?
Vue Interview Questions for Experienced Developers
  1. 42. How does Vue 3's reactivity system actually work?
  2. 43. How does Vue's virtual DOM and its compiler optimize rendering?
  3. 44. When would you use shallowRef, shallowReactive, or markRaw?
  4. 45. How does server-side rendering work in Vue, and why use Nuxt?
  5. 46. How do you diagnose and fix a slow Vue application?
  6. 47. How do you design composables that scale across a codebase?
  7. 48. What are the main differences between Vue 2 and Vue 3?
  8. 49. What is Suspense, and what state is it in?
  9. 50. How do you test Vue components?
  10. 51. How do you decide where state should live in a large Vue app?
  11. 52. What is effectScope, and where is it useful?
  12. 53. What is a custom renderer, and why does Vue expose one?
  13. 54. What are the patterns for data fetching in Vue, and their trade-offs?
  14. 55. What are the main security concerns in a Vue app?
  15. 56. How would you design a reusable component library in Vue?
  16. 57. What causes a hydration mismatch, and how do you fix it?
  17. 58. How do Vue plugins work?
  18. 59. What does v-memo do, and when is it worth using?
  19. 60. What is watcher flush timing, and why does the flush option matter?

Vue Interview Questions for Freshers

Freshers21 questions

The fundamentals every entry-level round checks. If any answer here surprises you, that's your study list.

Q1. What is Vue and what makes it progressive?

Vue is a JavaScript framework for building user interfaces, based on a reactive data model and reusable components. Progressive means you can adopt it in stages: sprinkle it onto one page, or scale up to a full single-page app with routing and state management.

Its draw is a gentle learning curve and single-file components that keep template, script, and styles for one component in one file. You bind data in a template, and Vue updates the DOM automatically when that data changes.

Key point: A one-line definition plus the word 'reactive' and one adoption example beats a feature list. Interviewers open with this to hear how you organize an answer.

Watch a deeper explanation

Video: Vue JS 3 Tutorial for Beginners #1 - Introduction (The Net Ninja, YouTube)

Q2. What is reactivity in Vue, and how do you declare reactive state?

Reactivity means the view updates automatically when the underlying data changes. In Vue 3 you declare reactive state with ref() for single values and reactive() for objects, and Vue tracks which parts of the template depend on which pieces of state.

When you change a ref's .value or a reactive object's property, Vue re-runs only the parts of the render that used it. You never manually touch the DOM.

javascript
import { ref, reactive } from 'vue'

const count = ref(0)          // single value
const user = reactive({ name: 'Asha', age: 31 })

count.value++                 // .value in script
user.age = 32                 // property directly
// the template updates automatically

Key point: Saying 'the template re-renders only where the data is used' shows you understand tracking, not just that data is 'live'.

Q3. What is the difference between ref and reactive?

ref() wraps any value (primitive or object) in a reactive box you read and write through .value in script. reactive() takes an object and returns a reactive proxy you use directly, with no .value.

Use ref for primitives and when you need to reassign the whole value; use reactive for objects when you won't replace the reference. A common gotcha: destructuring a reactive object breaks reactivity, while refs survive destructuring.

javascript
import { ref, reactive } from 'vue'

const n = ref(0)
n.value = 5                    // ref: use .value

const state = reactive({ n: 0 })
state.n = 5                    // reactive: direct

const { n: broken } = reactive({ n: 0 })  // loses reactivity
refreactive
WrapsAny valueObjects only
Accessvalue.valuevalue.prop
Reassign whole valueYesNo (breaks reactivity)
Survives destructuringYesNo

Key point: The follow-up is 'why does destructuring reactive break it?'. Answer: the proxy is on the object, and destructuring copies plain values out.

Q4. What is a computed property, and how does it differ from a method?

A computed property derives a value from reactive state and caches the result. It only recomputes when one of its reactive dependencies changes, so reading it repeatedly is free between changes.

A method called in the template runs on every re-render, whether its inputs changed or not. Use computed for derived values you display; use a method for actions triggered by events.

javascript
import { ref, computed } from 'vue'

const price = ref(100)
const qty = ref(3)

const total = computed(() => price.value * qty.value)  // cached
// total recomputes only when price or qty changes

Key point: The word the question needs is 'cached'. Mention it and the reason (dependency tracking) and this question is done.

Q5. When do you use computed versus watch?

Use computed when you need a new value derived from existing state: a total, a filtered list, a formatted string. It's declarative and cached.

Use watch when a state change should trigger a side effect: calling an API, saving to storage, or manually reacting to a change. If you find yourself setting state inside a computed getter, you actually wanted a watcher.

javascript
import { ref, computed, watch } from 'vue'

const query = ref('')
const upper = computed(() => query.value.toUpperCase())  // derived value

watch(query, (newVal) => {
  fetchResults(newVal)   // side effect on change
})

Key point: The rule of thumb the technical value is: computed for a value, watch for an action.

Q6. What are directives in Vue, and can you name the common ones?

Directives are special attributes starting with v- that tell Vue to do something to an element. The everyday set: v-bind (bind an attribute), v-on (listen to an event), v-model (two-way binding), v-if / v-else / v-show (conditional rendering), and v-for (list rendering).

They have shorthands you'll see everywhere: colon for v-bind (:href) and at-sign for v-on (@click). You can also write custom directives for low-level DOM access.

html
<a :href="url">link</a>            <!-- v-bind shorthand -->
<button @click="save">Save</button> <!-- v-on shorthand -->
<input v-model="name" />            <!-- two-way binding -->
<li v-for="item in items" :key="item.id">{{ item.label }}</li>

Q7. What is the difference between v-if and v-show?

v-if conditionally adds or removes the element from the DOM entirely: when false, the element and its component aren't rendered at all. v-show always renders the element and toggles it with the CSS display property.

v-if has higher toggle cost but no initial cost when false; v-show has higher initial cost but cheap toggling. Use v-if for things that rarely change, v-show for things toggled often like a dropdown.

v-ifv-show
When falseElement removed from DOMElement stays, display: none
Toggle costHigher (mount/unmount)Lower (CSS only)
Initial cost if falseNoneStill renders
Best forRarely toggledFrequently toggled

Key point: The right coverage names the trade-off both ways, not just 'one removes and one hides'.

Q8. How does v-for work, and why does it need a key?

v-for renders a list by iterating an array, object, or range: v-for="item in items". You bind a unique key to each item so Vue can track identity across updates.

Without a stable key, Vue patches nodes in place by index, which corrupts state (like input focus or checkbox state) when the list reorders or items are inserted. Use a stable unique id, never the array index when the list can change.

html
<ul>
  <li v-for="user in users" :key="user.id">
    {{ user.name }}
  </li>
</ul>
<!-- key = user.id, not the loop index -->

Key point: 'Why not use the index as key?' is the standard follow-up. Answer: it breaks on reorder or insertion because the identity shifts.

Q9. What does v-model do, and what is it sugar for?

v-model creates two-way binding on a form element: it binds the value and listens for input in one directive, so the state and the field stay in sync.

Under the hood on a text input it's shorthand for :value plus @input updating that value. On a custom component in Vue 3, v-model binds modelValue and listens for update:modelValue.

html
<!-- these two are equivalent on a text input -->
<input v-model="text" />
<input :value="text" @input="text = $event.target.value" />

Q10. What are props, and what are the rules for using them?

Props are how a parent passes data down to a child component. You declare them in the child, and the parent binds values with attributes. Props are one-way: data flows down, and a child should not mutate a prop directly.

If a child needs to change a prop's value, it emits an event so the parent updates the source, or it copies the prop into local state. Declaring prop types and defaults documents the component and catches mistakes.

javascript
// child
defineProps({
  title: { type: String, required: true },
  count: { type: Number, default: 0 },
})

Key point: The one-way rule (never mutate a prop) is what this question really checks. State it directly.

Q11. How does a child component communicate back to its parent?

The child emits a custom event, and the parent listens for it with v-on. This keeps data flow predictable: props down, events up. In Vue 3 you declare emitted events with defineEmits and call the returned emit function.

The parent binds a handler like @save="onSave" and receives whatever payload the child passed to emit.

javascript
// child
const emit = defineEmits(['save'])
function submit() {
  emit('save', { id: 1, name: 'Asha' })
}

// parent template:
// <UserForm @save="onSave" />

Q12. What are the main lifecycle hooks in Vue 3?

Lifecycle hooks run at set points in a component's life: onBeforeMount and onMounted (DOM insertion), onBeforeUpdate and onUpdated (reactive change causes a re-render), and onBeforeUnmount and onUnmounted (teardown).

The two you use most: onMounted for work needing the real DOM or a first data fetch, and onUnmounted to clean up timers, listeners, or subscriptions so they don't leak.

javascript
import { onMounted, onUnmounted } from 'vue'

onMounted(() => {
  const id = setInterval(tick, 1000)
  onUnmounted(() => clearInterval(id))  // clean up
})

Key point: the key signal is cleanup awareness. Pairing onMounted setup with onUnmounted teardown is the signal.

Q13. What is a Single-File Component (SFC)?

A single-file component is a .vue file that holds a component's template, script, and styles together. The template block is the markup, the script block is the logic, and an optional style block holds CSS that can be scoped to the component.

This colocation keeps everything about one component in one place. With script setup, the script block is more concise: top-level bindings are automatically exposed to the template.

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

<template>
  <button @click="count++">{{ count }}</button>
</template>

<style scoped>
button { padding: 8px; }
</style>

Q14. How does template interpolation and attribute binding work?

Text interpolation uses double curly braces: {{ expression }} renders a reactive value as text and updates when it changes. For attributes you can't use curly braces; you use v-bind (or its colon shorthand) instead.

Templates accept single JavaScript expressions, not statements, so you can call a method or do a ternary but not write an if block or a loop inline.

html
<h1>{{ title }}</h1>
<span>{{ isNew ? 'New' : 'Old' }}</span>
<img :src="imageUrl" :alt="title" />

Q15. How do you handle events, and what are event modifiers?

You listen with v-on or its @ shorthand: @click="handler". The handler can be a method name or an inline expression. Event modifiers are suffixes that handle common needs without boilerplate.

Common modifiers: .prevent (calls preventDefault), .stop (stops propagation), .once (fires once), and key modifiers like @keyup.enter. They keep handlers focused on logic instead of DOM plumbing.

html
<form @submit.prevent="onSubmit">
  <input @keyup.enter="search" />
  <button @click.once="init">Start</button>
</form>

Q16. How do you bind classes and styles dynamically?

Bind class with :class using an object (class name to boolean) or an array. Bind style with :style using an object of CSS properties. Vue merges these with any static class or style on the element.

The object syntax is the common one: a class is applied when its value is truthy, which reads cleanly for state-driven styling.

html
<div :class="{ active: isActive, error: hasError }">...</div>
<div :style="{ color: textColor, fontSize: size + 'px' }">...</div>

Q17. What is the difference between the Options API and the Composition API?

The Options API organizes a component by option type: a data function, a methods object, computed, watch, and lifecycle options. The Composition API organizes by logical concern inside setup (or script setup), using functions like ref, computed, and the on-hooks.

The Composition API's advantage shows on larger components: related logic stays together instead of scattering across option blocks, and you can extract reusable logic into composables. Vue 3 supports both.

javascript
// Composition API (script setup)
import { ref, computed } from 'vue'
const count = ref(0)
const double = computed(() => count.value * 2)
function inc() { count.value++ }

Key point: Say you can use both but new code favors Composition. Explaining 'related logic stays together' is the depth marker.

Q18. Why should you avoid using v-if and v-for on the same element?

In Vue 3, v-if has higher priority than v-for when on the same element, so the condition can't access the loop variable, which is usually the opposite of what people expect. It also mixes two concerns on one node.

The fix is to filter the list first with a computed property, or wrap the loop in a template tag with the v-if on an outer element. This keeps rendering intent clear.

html
<!-- avoid: v-if and v-for together -->
<!-- prefer: filter in a computed -->
<li v-for="u in activeUsers" :key="u.id">{{ u.name }}</li>
<!-- activeUsers = computed(() => users.filter(u => u.active)) -->

Q19. How do you scaffold a new Vue project today?

The current recommendation is create-vue, which sets up a Vite-based project: run npm create vue@latest, pick options like router, Pinia, and testing, and you get a fast dev server with hot module replacement.

Vite replaced the older Vue CLI (webpack-based) tooling as the default for its faster startup and builds. Vite by name matters.

bash
npm create vue@latest
# prompts for router, Pinia, TypeScript, tests
cd my-app && npm install && npm run dev

Q20. Is Vue's data flow one-way or two-way?

The data flow between components is one-way: props go down from parent to child, and the child reports changes up through events. v-model looks two-way but it's really that same pair (a value binding plus an event) bundled into one directive.

So the mental model is one-way binding with a two-way convenience layer on form fields. Keeping the flow one-way is what makes state changes traceable to a single source.

Key point: Interviewers ask this to catch the myth that Vue is 'two-way like Angular 1'. Explaining v-model as sugar over props-plus-events is the correct nuance.

Q21. Can a computed property be writable?

Yes. A computed is read-only by default, but you can pass an object with get and set functions to make it writable. The getter derives the value; the setter runs when you assign to the computed, usually updating the underlying state.

A common use is a full-name computed that splits an assignment back into first and last name, or wrapping a store value so a v-model can bind to it.

javascript
import { ref, computed } from 'vue'
const first = ref('Asha'), last = ref('Rao')
const fullName = computed({
  get: () => `${first.value} ${last.value}`,
  set: (val) => { [first.value, last.value] = val.split(' ') },
})
fullName.value = 'Ben Cole'   // updates first and last
Back to question list

Vue Intermediate Interview Questions

Intermediate20 questions

For candidates with working experience: reactivity mechanics, component patterns, and the questions that separate users from understanders.

Q22. How does script setup work, and what does it expose?

script setup is compile-time sugar for the Composition API in single-file components. Everything at the top level (imports, variables, functions) is automatically available to the template, so you skip a return statement and boilerplate.

It also gives compiler macros like defineProps, defineEmits, and defineExpose that don't need importing. It's the recommended way to write Vue 3 components.

html
<script setup>
import { ref } from 'vue'
const props = defineProps({ label: String })
const emit = defineEmits(['change'])
const value = ref('')
</script>

<template>
  <input v-model="value" :aria-label="props.label" />
</template>

Watch a deeper explanation

Video: Vue JS Crash Course (Traversy Media, YouTube)

Q23. What is a composable, and how do you write one?

A composable is a function that uses Composition API features to encapsulate and reuse stateful logic. By convention its name starts with use, like useMouse or useFetch. It returns reactive state and functions the component can bind to.

Composables are the Vue 3 answer to logic reuse: they replace mixins, which suffered from name collisions and unclear sources. Each call gets its own isolated state.

javascript
// useCounter.js
import { ref } from 'vue'
export function useCounter(start = 0) {
  const count = ref(start)
  const inc = () => count.value++
  return { count, inc }
}

// component: const { count, inc } = useCounter(10)

Key point: 'How is this better than mixins?' is the follow-up. Answer: explicit sources, no name collisions, isolated state per call.

Watch a deeper explanation

Video: Vue.js Course for Beginners (freeCodeCamp.org, YouTube)

Q24. What is the difference between watch and watchEffect?

watch takes an explicit source (a ref, a getter, or an array) and a callback, giving you both the new and old values and running only when that source changes. It's lazy by default: it doesn't run until the first change.

watchEffect runs immediately and automatically tracks every reactive value used inside it, re-running when any of them change. Use watch when you need the old value or precise control; use watchEffect for effects that depend on several values you don't want to list.

javascript
import { ref, watch, watchEffect } from 'vue'
const a = ref(0), b = ref(0)

watch(a, (val, old) => console.log(val, old))  // explicit, lazy, old value

watchEffect(() => console.log(a.value + b.value))  // auto-tracks a and b, runs now
watchwatchEffect
SourceExplicitAuto-tracked from body
Runs immediatelyNo (lazy)Yes
Old valueAvailableNot available
Best forPrecise, need old valueMulti-dependency effects

Q25. What are slots, and how do named and scoped slots differ?

Slots let a parent pass template content into a child's layout. A default slot is a single placeholder; named slots let a child define multiple insertion points (header, body, footer) the parent fills by name.

Scoped slots pass data from the child back up to the slot content, so the parent can render child data its own way. That's the pattern behind flexible list and table components.

html
<!-- child: List.vue -->
<slot name="item" :row="row"></slot>

<!-- parent -->
<List>
  <template #item="{ row }">
    <strong>{{ row.name }}</strong>
  </template>
</List>

Key point: Being able to explain a scoped slot (child gives data to parent's template) separates people who've built reusable components from people who've only used them.

Q26. How do provide and inject work, and when should you use them?

provide lets a component supply a value, and any descendant, however deep, reads it with inject. It avoids prop drilling: passing a prop through many intermediate components that don't use it.

Use it for things a whole subtree needs, like a theme, a locale, or a form context. For app-wide state prefer a store like Pinia; provide/inject shines for component-library context, not global app state.

javascript
// ancestor
import { provide, ref } from 'vue'
const theme = ref('dark')
provide('theme', theme)

// deep descendant
import { inject } from 'vue'
const theme = inject('theme', 'light')  // default fallback

Q27. What reactivity limitations should you know about?

Vue 3's Proxy-based reactivity fixed most Vue 2 caveats: you can add and delete object properties and set array items by index and reactivity still works. But some traps remain worth naming.

Destructuring a reactive object loses reactivity, so use toRefs when you need to destructure. Replacing a reactive object's reference (state = newObj) breaks the link. And plain values passed out of setup won't be reactive unless wrapped in a ref.

javascript
import { reactive, toRefs } from 'vue'
const state = reactive({ a: 1, b: 2 })

const { a, b } = toRefs(state)  // keeps reactivity
// now a.value and b.value stay linked to state

Key point: Mentioning toRefs in practice is a strong intermediate signal. It shows you've hit the destructuring trap in real code.

Q28. How does Vue Router handle navigation and dynamic routes?

Vue Router maps URL paths to components. You define routes, render a router-view where matched components appear, and handle with router-link or programmatically via the router instance. Dynamic segments like /user/:id expose params to the component.

It supports nested routes, navigation guards for auth checks, and lazy-loaded route components for code splitting, which keeps the initial bundle small.

javascript
const routes = [
  { path: '/user/:id', component: () => import('./User.vue') },  // lazy
]

// in a component
import { useRoute } from 'vue-router'
const route = useRoute()
console.log(route.params.id)

Q29. What is Pinia, and how does a store work?

Pinia is the official state management library for Vue 3. A store holds state, getters (computed values), and actions (methods that change state). Any component can use the store, and its state is reactive across the whole app.

It's simpler than Vuex: no mutations, direct state changes inside actions, and strong TypeScript inference. You define a store with defineStore and call it inside setup.

javascript
import { defineStore } from 'pinia'
import { ref, computed } from 'vue'

export const useCart = defineStore('cart', () => {
  const items = ref([])
  const count = computed(() => items.value.length)
  function add(item) { items.value.push(item) }
  return { items, count, add }
})

Watch a deeper explanation

Video: Learn Vue.js - Full Course for Beginners (freeCodeCamp.org, YouTube)

Q30. How does Pinia compare to Vuex?

Pinia removed the parts of Vuex that added ceremony without value. Vuex separated synchronous mutations from asynchronous actions and used string-based module namespacing. Pinia drops mutations entirely: actions change state directly, and stores are flat modules with no namespace strings.

Pinia also has better TypeScript inference and a smaller API. Vuex still exists for legacy Vue 2 and Vue 3 apps, but new projects use Pinia.

PiniaVuex
MutationsNone (actions change state)Required, separate from actions
ModulesFlat, multiple storesNested with namespacing
TypeScriptInferred out of the boxNeeds extra typing work
Status in Vue 3RecommendedLegacy / maintenance

Q31. What are dynamic and async components?

A dynamic component uses the built-in component element with an :is binding to swap which component renders at runtime, useful for tabs or wizards. An async component is defined with defineAsyncComponent so its code loads on demand.

Async components enable route-level and component-level code splitting: the chunk downloads only when the component is actually needed, shrinking the initial bundle.

javascript
import { defineAsyncComponent } from 'vue'
const Chart = defineAsyncComponent(() => import('./Chart.vue'))

// template:
// <component :is="currentTab" />
// <Chart /> loads its chunk only when rendered

Q32. What does the KeepAlive component do?

KeepAlive caches component instances instead of destroying them when they're toggled or swapped out. Wrap a dynamic component or a router-view in it, and switching away then back restores the previous state instead of remounting fresh.

It's the tool for preserving form input, scroll position, or fetched data across tab switches. It exposes onActivated and onDeactivated hooks in place of mount and unmount for cached components.

html
<KeepAlive>
  <component :is="currentTab" />
</KeepAlive>
<!-- switching tabs keeps each tab's state -->

Q33. What is the Teleport component used for?

Teleport renders a component's markup at a different place in the DOM than where it's declared, while keeping it logically part of the component. You give it a target selector like body.

It solves the modal and tooltip problem: a dialog defined deep in the tree can render at the document root so parent CSS overflow, transforms, or z-index stacking don't clip it.

html
<Teleport to="body">
  <div class="modal" v-if="open">
    <slot />
  </div>
</Teleport>

Q34. When would you write a custom directive?

Write a custom directive when you need direct, reusable DOM manipulation that doesn't fit a component: autofocus on mount, click-outside detection, or integrating a plain DOM library. Directives get lifecycle hooks like mounted and updated with the raw element.

The guidance is to reach for components first and directives only for low-level DOM access, because directives operate outside Vue's usual data flow.

javascript
const vFocus = {
  mounted: (el) => el.focus(),
}
// usage: <input v-focus />

Q35. How do you approach form validation in Vue?

For small forms, bind fields with v-model, compute validity with computed properties, and show errors conditionally. For anything larger, teams use a library like VeeValidate or Vuelidate that handles rules, touched state, and error messages.

The key idea to convey is derived validity: errors and the submit-disabled state are computed from the current field values, not manually toggled, so they stay in sync automatically.

javascript
import { ref, computed } from 'vue'
const email = ref('')
const emailError = computed(() =>
  email.value.includes('@') ? '' : 'Enter a valid email'
)
const canSubmit = computed(() => !emailError.value)

Q36. How do transitions and animations work in Vue?

The built-in Transition component applies enter and leave CSS classes automatically when an element is inserted or removed, so you write the animation in CSS and Vue times the class changes. TransitionGroup does the same for lists, including move animations on reorder.

For JavaScript-driven animation you can hook into the transition events, or integrate a library. The point is Vue handles the mount and unmount timing for you.

html
<Transition name="fade">
  <p v-if="show">Hello</p>
</Transition>

<style>
.fade-enter-active, .fade-leave-active { transition: opacity 0.3s; }
.fade-enter-from, .fade-leave-to { opacity: 0; }
</style>

Q37. How do you implement v-model on a custom component?

In Vue 3 a component v-model binds a modelValue prop and emits update:modelValue. You accept the prop and emit the event on change; the parent uses v-model as usual. You can also name models (v-model:title) for multiple bindings.

In newer Vue 3 versions the defineModel macro collapses this to one line, which is worth mentioning as current syntax.

html
<script setup>
const props = defineProps(['modelValue'])
const emit = defineEmits(['update:modelValue'])
</script>
<template>
  <input :value="modelValue"
         @input="emit('update:modelValue', $event.target.value)" />
</template>

Q38. What are template refs, and how do you access a DOM element?

A template ref gives you a direct reference to a DOM element or child component. You declare a ref with the same name as the ref attribute, and after mount it holds the element. Access it in onMounted, never during setup, because the DOM doesn't exist yet.

Use it sparingly for things Vue's declarative model can't do: focusing an input, measuring size, or calling a child's exposed method.

html
<script setup>
import { ref, onMounted } from 'vue'
const inputEl = ref(null)
onMounted(() => inputEl.value.focus())
</script>
<template>
  <input ref="inputEl" />
</template>

Q39. What is nextTick, and when do you need it?

Vue batches reactive updates and applies DOM changes asynchronously for efficiency. nextTick returns a promise that resolves after Vue has flushed those updates to the DOM, so you can read the updated DOM reliably.

You need it when you change state and then immediately need to measure or act on the resulting DOM: after adding a list item, scroll to it; after showing an input, focus it.

javascript
import { nextTick, ref } from 'vue'
const items = ref([])
async function add() {
  items.value.push('new')
  await nextTick()          // DOM now updated
  scrollToBottom()
}

Q40. What is the difference between global and local component registration?

Local registration imports a component into the file that uses it, so it's only available there and can be tree-shaken if unused. Global registration with app.component makes it usable in every template without importing.

Prefer local registration in most apps: it makes dependencies explicit and keeps the bundle lean. Reserve global registration for a handful of truly ubiquitous components like a base button or icon.

Q41. How do you handle errors from child components?

onErrorCaptured lets a parent catch errors thrown during a descendant's render, lifecycle hooks, or event handlers, so you can log them and show a fallback instead of a blank screen. Returning false stops the error from propagating further.

At the app level, app.config.errorHandler catches everything not caught locally, which is where you send errors to a monitoring service.

javascript
import { onErrorCaptured, ref } from 'vue'
const failed = ref(false)
onErrorCaptured((err) => {
  failed.value = true
  report(err)
  return false   // stop propagation
})
Back to question list

Vue Interview Questions for Experienced Developers

Experienced19 questions

advanced rounds probe internals, architecture judgment, and production scars. Expect every answer here to draw a follow-up.

Q42. How does Vue 3's reactivity system actually work?

Vue 3 wraps reactive objects in a JavaScript Proxy that intercepts property reads and writes. On read, it tracks which effect (a render function or watcher) is currently running and records the dependency. On write, it triggers every effect that depended on that property.

This replaced Vue 2's Object.defineProperty approach, which couldn't detect new properties or index assignments and required workarounds. The Proxy sees all operations, which is why Vue 3's reactivity has far fewer caveats.

javascript
// the essence, simplified
function reactive(obj) {
  return new Proxy(obj, {
    get(t, key) { track(t, key); return t[key] },
    set(t, key, val) { t[key] = val; trigger(t, key); return true },
  })
}

Key point: Naming Proxy vs Object.defineProperty and one concrete caveat it fixed (new keys, array index) is the depth this question screens for.

Q43. How does Vue's virtual DOM and its compiler optimize rendering?

A render produces a virtual DOM tree, and Vue diffs the new tree against the old to patch only what changed. What sets Vue 3 apart is a compiler-informed virtual DOM: the template compiler analyzes markup at build time and marks which parts are static and which are dynamic.

Optimizations like static hoisting (create static nodes once) and patch flags (skip nodes with no dynamic bindings) mean the runtime diff only visits parts that can actually change, so Vue's rendering scales with dynamic content, not template size.

Q44. When would you use shallowRef, shallowReactive, or markRaw?

By default reactive() and ref() are deep: nested objects become reactive too, which costs proxy overhead. shallowRef and shallowReactive make only the top level reactive, useful for large data structures where you swap the whole value rather than mutating deep inside.

markRaw tells Vue never to make an object reactive at all, which matters when you store third-party class instances (a chart, a map, a big immutable dataset) that break or slow down when proxied.

javascript
import { shallowRef, markRaw } from 'vue'
const bigList = shallowRef(load())  // reactive only when replaced
const chart = markRaw(new ThirdPartyChart())  // never proxied

Q45. How does server-side rendering work in Vue, and why use Nuxt?

SSR renders components to an HTML string on the server, sends it to the browser for a fast first paint and better SEO, then hydrates: the client-side Vue app attaches to the existing markup and takes over interactivity. Vue provides the low-level server renderer, but wiring routing, data fetching, and hydration by hand is involved.

Nuxt is the meta-framework that handles all of it: file-based routing, server routes, data fetching that runs on server then client, and hydration. Most teams reach for Nuxt rather than assembling SSR from primitives.

Key point: The follow-up is often 'what is hydration mismatch?'. Answer: server and client render different markup, so Vue can't attach cleanly and warns or re-renders.

Q46. How do you diagnose and fix a slow Vue application?

Measure first with the Vue Devtools performance tab and the browser profiler to find what actually re-renders and how long it takes. Common culprits: a computed that isn't cached because it reads non-reactive data, huge lists rendered without virtualization, and deep reactivity on large objects.

Fixes in order of impact: virtualize long lists, use v-once or v-memo for expensive static subtrees, mark heavy data with shallowRef or markRaw, code-split routes and components, and check that keys are stable. The discipline (profile, don't guess) is what matters.

  • Virtualize long lists so only visible rows render.
  • Use v-memo to skip re-rendering subtrees whose dependencies didn't change.
  • Apply shallowRef or markRaw to large or third-party data.
  • Split routes and heavy components with async loading.

Q47. How do you design composables that scale across a codebase?

Keep each composable focused on one concern, return refs and functions rather than a reactive blob so consumers can destructure freely, and accept configuration as arguments. Clean up side effects (listeners, intervals, subscriptions) inside the composable using onUnmounted so callers don't have to.

The returned values clearly, avoid hidden global state unless intended, and let composables call other composables. The failure mode to name: a mega-composable that does five things and can't be reused, which is just a mixin with new syntax.

javascript
export function useEventListener(target, event, handler) {
  onMounted(() => target.addEventListener(event, handler))
  onUnmounted(() => target.removeEventListener(event, handler))
}

Q48. What are the main differences between Vue 2 and Vue 3?

Vue 3 rewrote reactivity on Proxy (removing the new-property and array-index caveats), added the Composition API, and made the framework tree-shakeable so unused features drop out of the bundle. It also supports multiple root nodes per template (fragments), Teleport, and Suspense.

Migration friction comes from breaking changes: the new app-creation API (createApp), removed filters, changed v-model conventions on components, and the global API reorganization. Large apps often migrate incrementally with the compat build.

AreaVue 2Vue 3
ReactivityObject.definePropertyProxy
Logic reuseMixinsComposables
Root nodesSingle requiredMultiple (fragments)
BundleFull frameworkTree-shakeable

Q49. What is Suspense, and what state is it in?

Suspense is a built-in component that coordinates async dependencies in a subtree: it shows a fallback slot while async setup or async components resolve, then swaps in the default slot. It lets you handle loading states declaratively instead of tracking a loading boolean per component.

The honest senior note: Suspense has been marked experimental for a long time and its API can change, so many teams still manage loading state manually or through their router and data layer. Knowing it exists and its status is the right answer.

Q50. How do you test Vue components?

Vue Test Utils with Vitest (or Jest) is the standard for component tests: mount a component, interact with it, and assert on rendered output or emitted events. Test behavior a user cares about (does clicking emit the event, does the list render) rather than internal implementation.

For flows across components, Cypress or Playwright drive a real browser. A strong answer separates the layers: unit test composables as plain functions, component-test rendering and events, and end-to-end test critical user journeys.

javascript
import { mount } from '@vue/test-utils'
import Counter from './Counter.vue'

test('increments on click', async () => {
  const wrapper = mount(Counter)
  await wrapper.find('button').trigger('click')
  expect(wrapper.text()).toContain('1')
})

Q51. How do you decide where state should live in a large Vue app?

the narrowest scope that works and widen only when needed: local component state for UI concerns, props and events between a parent and its children, provide/inject for a subtree's shared context, and a Pinia store for state many unrelated parts of the app read or write comes first.

The anti-pattern to name is putting everything in the global store, which turns local UI state into global coupling and makes components hard to reuse. Server data is its own category: a query layer like TanStack Query handles caching and refetching better than a hand-rolled store.

Choosing where state lives

1Local state
used by one component only: ref in setup
2Props and emits
shared between a parent and its direct children
3Provide / inject
shared across a subtree: theme, form context
4Pinia store
read or written by many unrelated parts of the app

Server data (API responses) is a separate concern: a caching query layer usually beats storing it in a global store by hand.

Q52. What is effectScope, and where is it useful?

effectScope captures the reactive effects (computeds, watchers) created inside it so you can dispose of them all at once with scope.stop(). Composables used outside a component (in a store, a plugin, or shared singleton logic) don't get automatic cleanup, and effectScope provides it.

It's an advanced tool: most component-scoped effects are cleaned up automatically on unmount. You reach for effectScope when reactive logic lives outside a component's lifecycle and would otherwise leak watchers.

javascript
import { effectScope, watch } from 'vue'
const scope = effectScope()
scope.run(() => {
  watch(source, handler)   // tracked by the scope
})
scope.stop()   // disposes every effect created inside

Q53. What is a custom renderer, and why does Vue expose one?

Vue 3 split its runtime into a platform-agnostic core and a renderer. createRenderer lets you target something other than the browser DOM: native mobile, canvas, terminal, or a game engine, by supplying platform-specific create, patch, and remove operations.

You almost never write one directly, but it's why projects can render Vue components to non-DOM targets, and knowing the runtime is decoupled from the DOM is the architectural point interviewers probe.

Q54. What are the patterns for data fetching in Vue, and their trade-offs?

The simple pattern is fetch in onMounted into a ref, tracking loading and error state by hand. It works but you reimplement caching, refetching, and race handling in every component. A query library (TanStack Query for Vue) handles caching, deduplication, background refetch, and stale-while-revalidate for you.

For SSR apps, fetching moves into the framework's data layer (Nuxt's useFetch or useAsyncData) so it runs on the server for the first render and hydrates without a second request. Matching the pattern to the app is the judgment being tested.

Q55. What are the main security concerns in a Vue app?

Vue escapes interpolated content by default, so {{ }} is safe against XSS. The danger is v-html, which renders raw HTML: never pass user-controlled content to it without sanitizing, or you open a script-injection hole. The same care applies to binding user input into URLs or href attributes.

Beyond rendering: keep secrets out of the client bundle (anything shipped to the browser is public), validate on the server not just in the form, and set a Content Security Policy. Client-side checks are UX, not security.

Key point: Naming v-html as the specific XSS vector, and 'client checks are UX not security', is what separates a real answer from a generic one.

Q56. How would you design a reusable component library in Vue?

Design components to be controlled and flexible: expose behavior through props and events, use slots (especially scoped slots) so consumers control markup, and avoid baking in app-specific assumptions. Forward attributes and listeners so a base input behaves like a native one, and support v-model where two-way binding is expected.

Ship with clear TypeScript types, theming via CSS variables rather than hard-coded styles, and per-component imports so consumers tree-shake. Document the contract and version breaking changes carefully, because a library's API is a promise to every consumer.

Q57. What causes a hydration mismatch, and how do you fix it?

A hydration mismatch happens when the HTML the server rendered doesn't match what the client would render on first pass, so Vue can't cleanly attach event listeners and either warns or discards and re-renders that subtree. Common causes: rendering the current time or random values, reading window or localStorage during render, and invalid HTML nesting the browser silently fixes.

The fix is to make the first client render deterministic and identical to the server's: move browser-only logic into onMounted, guard access to browser globals, and use a client-only wrapper for content that genuinely can't render on the server.

Q58. How do Vue plugins work?

A plugin is an object with an install function (or a plain function) that receives the app instance. Inside, it can register global components and directives, add global properties, provide values, or configure the app. You activate it with app.use(plugin, options).

Vue Router, Pinia, and i18n are all plugins. Writing one is how you package cross-cutting setup so an app enables it in a single line rather than wiring it in many places.

javascript
const myPlugin = {
  install(app, options) {
    app.provide('config', options)
    app.directive('focus', { mounted: el => el.focus() })
  },
}
// main.js: app.use(myPlugin, { theme: 'dark' })

Q59. What does v-memo do, and when is it worth using?

v-memo memoizes a subtree of the template: you give it an array of dependencies, and Vue skips re-rendering that subtree unless one of those values changed. It's a manual escape hatch for cases where Vue's own diffing still costs too much.

Its main payoff is large lists where each row is expensive and only a few rows change. Reach for it only after profiling shows the render is the bottleneck, because a wrong dependency array causes stale UI that's hard to trace.

html
<div v-for="item in list" :key="item.id"
     v-memo="[item.id === selectedId]">
  <!-- re-renders only when its selected state flips -->
</div>

Key point: The trap answer is using v-memo everywhere. The right coverage names it as a last-resort, post-profiling tool for large lists, with the stale-UI risk.

Q60. What is watcher flush timing, and why does the flush option matter?

By default watchers run before Vue updates the DOM (pre flush), so if a watcher reads the DOM it sees the old state. Setting flush: 'post' runs the watcher after the DOM update, which you need when the callback measures or manipulates the freshly rendered DOM.

There's also flush: 'sync' that fires synchronously on every change, rarely wanted because it can run many times per tick and hurt performance. Knowing pre versus post is what prevents 'my watcher sees stale DOM' bugs.

javascript
import { watch, ref } from 'vue'
const items = ref([])
watch(items, () => {
  measureLayout()   // reads updated DOM
}, { flush: 'post' })   // run after DOM patch
Back to question list

Why Vue? Vue vs React, Angular, and Svelte

Vue sits between React's minimal core and Angular's batteries-included framework: it gives you an official router, state library, and build tooling without forcing a rigid structure. It picks a gentle learning curve and single-file components as its main bet, so teams get productive fast on readable code. Where it competes hardest is against React for market share and against Svelte for compiled output size. Knowing these trade-offs out loud is itself an interview signal: it shows you pick tools on merits, not habit.

FrameworkReactivity modelBest atWatch out for
VueProxy-based, automatic trackingFast onboarding, single-file componentsSmaller hiring pool than React
ReactExplicit re-render, hooksHuge ecosystem and job marketMore boilerplate, manual memoization
AngularZone.js / signalsLarge enterprise apps, opinionatedSteep curve, heavier framework
SvelteCompile-time reactivitySmall bundles, no virtual DOMSmaller ecosystem, fewer libraries

How to Prepare for a Vue Interview

Prepare in layers, and practice out loud. Most Vue rounds move from concept questions to live coding to a design or debugging discussion, so rehearse each stage rather than only reading answers.

  • Master your tier's concepts until you can explain them without notes, then read one tier up for the stretch questions.
  • Type and run every snippet in a Vue SFC playground; modifying working reactive code cements it far faster than reading.
  • Practice thinking aloud on small component problems with a timer, because your process, not just the final answer is the technical point.
  • Take the quiz on this page to find weak spots, then revisit those questions before the real round.

The typical Vue interview flow

1Recruiter or phone screen
background, motivation, a few concept checks
2Technical concepts
reactivity, computed vs watch, props and emits, lifecycle
3Live coding
build or fix a component under observation
4Design or debugging
state architecture, performance, reading unfamiliar code

Earlier rounds increasingly run as AI coding interviews. The concepts on this page are what gets probed at every stage.

Test Yourself: Vue Quiz

Ready to test your Vue knowledge?

10 questions, about 6 minutes. Score 70% or higher to earn a shareable certificate.

10 questions Instant feedback Free certificate on 70%+

Frequently  Asked  Questions

Which Vue topics should I prioritize before a technical round?

Prioritize the areas that change real decisions: fundamentals, practical tasks, debugging, trade-offs, and evidence. The highest-value questions are tied to a project example, a validation check, or a failure mode.

Which Vue version do these answers assume?

Vue 3, throughout, with the Composition API where it matters. Vue 2 reached end of life at the end of 2023, so most teams have migrated or are migrating. Where a Vue 2 difference is worth knowing, like the reactivity caveats or the switch from filters, the answer says so. When in doubt in an interview, say you're answering for Vue 3.

Options API or Composition API: which should I learn for interviews?

Learn both, lead with the Composition API. New Vue 3 code and most job postings favor the Composition API and composables, but plenty of production codebases still use the Options API, so interviewers may ask you to read or convert it. Being fluent in both, and able to explain why a team might pick one, is the answer that lands.

Are these questions enough to pass a Vue interview?

They cover the question-answer portion well, but most Vue rounds also include live coding: building or fixing a component under observation while explaining your thinking. solving small problems out loud with a timer is the practice path. Our AI coding interview prep guide covers that format, including how automated evaluation reads your process.

How long does it take to prepare for a Vue interview?

If you use Vue at work or in study, one to two weeks of an hour a day covers this bank with practice runs. Starting colder, plan three to four weeks and build small components daily; reading answers without writing templates and reactive code is how preparation quietly fails.

Is there a way to test my Vue knowledge?

Yes. The quiz on this page scores you as you go and explains every answer, so you find your weak spots fast. Pass the threshold and you can download a shareable certificate with your name and score, free and with no sign-up. It's the quickest way to check yourself before the real round.

From the team that builds coding interviews

Hyring builds the AI Coding Interviewer that runs live technical rounds in 20+ languages for 5,000+ hiring teams. These Vue questions and scoring notes reflect what actually gets asked and evaluated inside the interviews we host.

See how the AI Coding Interviewer works

Sources

Adithyan RKWritten by Adithyan RK
Surya N
Fact-checked by Surya N
Published on: 6 May 2026Last updated: 23 Jun 2026
Share: