Vue interview questions: real-world lessons and examples
If you're preparing for a Vue role or conducting interviews, you've probably noticed that good Vue developers don't just know the syntax—they understand the underlying mechanics that make the framework tick. We've found that the most valuable interview conversations focus on Vue interview questions: real-world lessons and examples that reveal how candidates think about state, side effects, and component architecture.
In this piece, we'll walk through four questions we ask regularly in technical interviews, with real production scenarios and code examples that show the thinking patterns we look for in senior engineers.
How does reactivity work? — Proxies track dependencies and trigger effects
This is often the first deep dive we have with candidates. The question sounds simple: "How does Vue track changes?" But the answer separates engineers who've read the docs from those who've debugged production reactivity bugs.
Vue 3 uses JavaScript Proxies to intercept property access and mutations. When you access a property in a template or computed, Vue wraps that read as a dependency. When you mutate that property, the Proxy triggers effects (re-renders, watchers, computed updates).
Here's a realistic scenario: a product listing component that fetches items and filters them. You're tracking both the raw API response and a filtered list.
import { ref, computed, watch } from 'vue'
export default {
setup() {
const products = ref([])
const searchTerm = ref('')
// This computed depends on products and searchTerm.
// When either changes, Vue re-runs this function.
const filtered = computed(() => {
return products.value.filter(p =>
p.name.toLowerCase().includes(searchTerm.value.toLowerCase())
)
})
// Watch tracks dependencies too, but runs side effects.
watch(
() => searchTerm.value,
(newTerm) => {
console.log(`User searched for: ${newTerm}`)
// Log analytics, fire API call, etc.
}
)
return { products, searchTerm, filtered }
}
}
The key point candidates should understand: accessing a property inside a computed or watch function registers it as a dependency. If you don't access it, Vue won't track it. This trips up engineers coming from React who expect props to auto-track without explicit reads.
We ask follow-up questions like: "What happens if you mutate an object property without using a ref or reactive wrapper?" or "Why can't Vue detect array index assignment?" These reveal whether they've hit reactivity gotchas in real work.
ref vs reactive? — ref wraps any value; reactive is for objects
This question gets at API design thinking. Both ref and reactive create reactive state, but they solve different problems.
ref wraps any value—primitives, objects, arrays—in an object with a .value property. reactive only works with objects and doesn't need .value because it returns a proxy directly.
In production, the choice matters for code clarity and ergonomics. Consider a shopping cart:
import { ref, reactive } from 'vue'
// Pattern 1: Everything in refs (more boilerplate, more explicit)
const cartItems = ref([])
const totalPrice = ref(0)
const isLoading = ref(false)
// Pattern 2: Related state in reactive (less .value, feels like plain object)
const cart = reactive({
items: [],
totalPrice: 0,
isLoading: false,
discountCode: null
})
// Updating is different:
cartItems.value.push(item) // ref pattern
cart.items.push(item) // reactive pattern
// Template access:
// With ref: {{ cartItems }} (Vue auto-unwraps in templates)
// With reactive: {{ cart.items }}
In a real team, we've seen the best practices emerge:
- Use
refat the top level of a component setup. It's explicit when you see.value, and it works well with destructuring. - Use
reactivefor cohesive form state or grouped domain objects. Fewer dots, reads more naturally. - Avoid mixing heavily. Pick a pattern per component to keep the team aligned.
Good candidates explain the tradeoffs. Great ones say, "I've seen teams get confused by mixing both patterns. We standardize on X because..."
computed vs watch? — Derived values vs side effects
This separates people who've shipped features from those still learning. Both respond to dependency changes, but they're fundamentally different in purpose.
Computed is for deriving values. It's synchronous, cached, and idempotent. Watch is for side effects: logging, API calls, DOM updates, analytics. It can be async and runs every time (unless you use specific trigger modes).
Real scenario: a dashboard that shows user stats and needs to log when filters change.
import { ref, computed, watch } from 'vue'
export default {
setup() {
const userId = ref(1)
const dateRange = ref({ start: '', end: '' })
const stats = ref(null)
// Computed: derive a display string. Runs only if userId changes.
const userLabel = computed(() => {
return `User #${userId.value}`
})
// Watch: side effect when userId changes. Could be async.
watch(
() => userId.value,
async (newId) => {
console.log(`Fetching stats for user ${newId}`)
stats.value = await fetchUserStats(newId)
}
)
// Watch with multiple dependencies.
watch(
[() => userId.value, () => dateRange.value],
async ([newId, newRange]) => {
// Debounce or cancel previous request in production.
const result = await fetchStats(newId, newRange)
stats.value = result
}
)
return { userId, dateRange, userLabel, stats }
}
}
Common confusion: "Should I use computed or watch?" If you're calculating a new value for the template, use computed. If you're reacting to a change and doing something else (fetching, logging), use watch.
We ask: "What's the performance difference?" and "When would you use watchEffect?" Candidates who've tuned real dashboards know that computed caching matters, and that watch can leak memory if not cleaned up (especially with watchers in loops).
How do components communicate? — Props, emits, provide/inject and stores
This is where architecture thinking shows up. Vue gives you multiple tools: props (down), emits (up), provide/inject (skip levels), and stores (global state). Knowing which to use prevents spaghetti code.
Let's model a real feature: a modal dialog for confirming a purchase, nested three levels deep inside a product form.
// PurchaseModal.vue - innermost child
// ProductForm.vue - middle component
// Top-level App.vue
This is the "props down, events up" pattern. It works for shallow hierarchies, but for deeply nested trees or globally needed state, it gets verbose.
That's where provide/inject helps for intermediate state that doesn't need to be global:
// Somewhere mid-tree, provide configuration
// Deep child can inject without prop drilling
And stores (Pinia in modern Vue) for truly global state: authentication, theme, notifications.
We ask: "If you had to move this feature to a different part of the app, how would you refactor communication?" Senior candidates think about coupling and maintainability, not just "does it work today."
Common mistakes
- Mutating refs without .value in setup. Candidates forget that
const x = ref(0)needsx.value++, notx++. - Putting side effects in computed. We see candidates add
console.logor API calls inside computed functions, which breaks the purity assumption. - Ignoring watch cleanup. Creating watchers in loops or on demand can leak memory. Ask: "How do you stop a watcher?"
- Prop drilling when provide/inject is clearer. Some teams resist provide/inject out of unfamiliarity, then pass 7 props through 5 levels.
- Mixing reactive and ref inconsistently. A team that uses both with no convention creates friction and bugs during refactors.
Key takeaways
- Reactivity in Vue 3 relies on Proxies and dependency tracking during render and watchers. Accessing a property registers it; not accessing it means Vue won't track it.
refis for any value with explicit.valueaccess;reactiveis for object-shaped state without the extra property. Choose one pattern per team and stick to it.computedis for derived values and caching;watchis for side effects. If you're calculating a display value, use computed. If you're fetching or logging, use watch.- Communication patterns scale: props/emits for shallow trees, provide/inject to skip levels, stores for global state. Think about future maintainability, not just today's structure.
- The best Vue developers we work with ask "what happens when this scales?" and "how does a new teammate debug this?" as much as they ask "does this work?"






Comments
Loading comments…