Vue.js Interview Questions and Answers 2026
Vue.js is a progressive JavaScript framework for building user interfaces. Candidates report that reactivity internals, Composition API patterns, Vue Router...

What changed in 2026 drives
Mass-recruiter offer letters are flatter for 2026 batch - the 4-5 LPA ASE band has barely budged in three years while inflation eats real wages. Premium tracks (Digital, Pro, Elite, Specialist) are still where the differential lives, and they are entirely test-driven. If you are aiming higher than the default offer, the coding round is not optional pageantry - it is the entire interview.
What I'd actually study for this
- 01Two solid coding-round answers (1 medium-hard DSA each, with edge-case discussion) > five half-baked ones
- 02One real project you can defend end-to-end - file paths, design decisions, and what you would change
- 03One DBMS schema you actually built (not a textbook ER diagram), with at least 3 join-heavy queries written from memory
- 04Three behavioural STAR stories: failure recovered, conflict handled, ownership taken
Where most candidates trip up
The single biggest mistake is treating company-specific guides as primary prep and DSA as secondary. It is the opposite. Mass recruiters use the test as a filter, but premium tracks at every IT services company use coding to allocate offer band. Spend 70% of prep time on DSA + system fundamentals, 20% on company-specific patterns, 10% on HR rehearsal. Reverse that ratio and you collect the default offer.
Editorial commentary by Aditya Sharma · written for PapersAdda · not generated, not aggregated.
Vue.js is a progressive JavaScript framework for building user interfaces. Candidates report that reactivity internals, Composition API patterns, Vue Router navigation guards, and Pinia state management are the most frequently tested topics in frontend engineering and full-stack developer interviews. This guide covers 50 questions ranging from Vue 3 fundamentals to advanced patterns. Always confirm role-specific framework requirements on the official careers portal of the company you are targeting.
Table of Contents
- Vue 3 Fundamentals
- Reactivity System
- Composition API
- Components and Props
- Vue Router
- Pinia State Management
- Performance and Advanced Patterns
- 5-Question Mock Test
- Frequently Asked Questions
Vue 3 Fundamentals
Q1. What is Vue 3 and what are its key improvements over Vue 2? Easy
Vue 3 is a complete rewrite of the framework released in 2020. Key improvements include the Composition API for better logic reuse, a Proxy-based reactivity system replacing Object.defineProperty, multiple root elements per template (Fragments), Teleport for rendering outside the component tree, Suspense for async component handling, improved TypeScript support, and a smaller bundle size through tree-shaking. The virtual DOM was rewritten for better performance with static hoisting and patch flags.
Q2. What is the Vue instance lifecycle? Easy
Vue 3 lifecycle hooks in order:
| Hook | Composition API | Description |
|---|---|---|
| Before create | (setup itself) | Instance initializing |
| Created | (after setup) | Reactive data ready, no DOM |
| beforeMount | onBeforeMount | Template compiled, not yet in DOM |
| mounted | onMounted | Component in DOM |
| beforeUpdate | onBeforeUpdate | Data changed, before re-render |
| updated | onUpdated | DOM updated |
| beforeUnmount | onBeforeUnmount | Teardown starts |
| unmounted | onUnmounted | Component removed |
import { onMounted, onBeforeUnmount } from 'vue'
export default {
setup() {
onMounted(() => {
console.log('Component mounted')
})
onBeforeUnmount(() => {
console.log('Cleanup here')
})
}
}
Q3. What is the difference between v-show and v-if? Easy
v-if conditionally renders the element. When false, the element and its children are not in the DOM at all. It has higher toggle cost but lower initial cost when condition starts false.
v-show always renders the element but toggles CSS display: none. It has lower toggle cost but always pays the initial render cost.
Use v-if for conditions that rarely change; use v-show for frequently toggled content.
Q4. Explain Vue's template compilation. Medium
Vue templates are compiled to render functions (JavaScript) at build time (or runtime in browser builds). The compiler analyzes the template and generates optimized VNode factory functions. In Vue 3, the compiler adds patch flags to VNodes indicating what can change (props, text, class, style) so the runtime can skip diffing static parts. Static nodes are hoisted outside the render function to avoid re-creation.
// Template: <div class="static">{{ message }}</div>
// Compiles to approximately:
import { createElementVNode, toDisplayString, openBlock, createElementBlock } from 'vue'
const _hoisted_1 = { class: 'static' }
function render(_ctx) {
return (openBlock(), createElementBlock('div', _hoisted_1,
toDisplayString(_ctx.message), 1 /* TEXT patch flag */
))
}
Q5. What are Vue directives and how do you create a custom directive? Medium
Directives are special attributes prefixed with v- that apply reactive behavior to the DOM. Built-in: v-if, v-for, v-bind, v-on, v-model, v-show, v-slot.
Custom directive with lifecycle hooks:
// Global registration
app.directive('focus', {
mounted(el, binding) {
el.focus()
if (binding.value) {
el.style.outline = binding.value
}
},
updated(el, binding) {
if (binding.value !== binding.oldValue) {
el.style.outline = binding.value
}
}
})
// Usage: <input v-focus="'2px solid blue'" />
Reactivity System
Q6. How does Vue 3's reactivity system work? Medium
Vue 3 uses JavaScript Proxy to intercept get/set operations on reactive objects. When a reactive property is accessed during component rendering, Vue tracks the dependency. When the property is mutated, Vue triggers re-renders for all components that depend on it.
function reactive(obj) {
return new Proxy(obj, {
get(target, key) {
track(target, key) // record dependency
return target[key]
},
set(target, key, value) {
target[key] = value
trigger(target, key) // notify dependents
return true
}
})
}
Vue 2 used Object.defineProperty which could not detect new property additions or array index mutations, requiring Vue.set().
Q7. What is the difference between ref and reactive? Easy
| Feature | ref | reactive |
|---|---|---|
| Accepts | Any type (primitive or object) | Objects/arrays only |
| Access | .value in JS, auto-unwrapped in template | Direct property access |
| Destructuring | Safe (still reactive) | Loses reactivity |
| Replace entirely | Supported (reassign .value) | Not supported |
| TypeScript | Ref<T> type | Plain object type |
import { ref, reactive } from 'vue'
const count = ref(0)
count.value++ // JS
// {{ count }} in template (auto-unwrapped)
const state = reactive({ count: 0, name: 'Vue' })
state.count++ // direct access
const { count } = state // BUG: count is no longer reactive
const { count: countRef } = toRefs(state) // CORRECT
Q8. What is computed and how does it differ from a method? Easy
computed is cached based on its reactive dependencies. It only re-evaluates when a dependency changes. A method re-runs every time the component re-renders.
import { ref, computed } from 'vue'
const items = ref([1, 2, 3, 4, 5])
// Cached: only recomputes when items changes
const total = computed(() => items.value.reduce((a, b) => a + b, 0))
// Runs on every render even if items hasn't changed
function getTotal() {
return items.value.reduce((a, b) => a + b, 0)
}
Use computed for derived state; use methods for event handlers or operations with side effects.
Q9. Predict the output: Medium
import { ref, watch } from 'vue'
const count = ref(0)
watch(count, (newVal, oldVal) => {
console.log(`${oldVal} -> ${newVal}`)
})
count.value = 1
count.value = 2
Output:
0 -> 1
1 -> 2
Explanation: watch by default is lazy (does not run on initial value) and runs after each synchronous mutation. Each assignment triggers the watcher with the previous and new values. The watcher is not batched here because each mutation is a separate operation that triggers Vue's scheduler.
Q10. What is watchEffect and when do you use it over watch? Medium
watchEffect automatically tracks dependencies accessed during its execution and re-runs when any of them change. It runs immediately on setup (eager by default).
import { ref, watchEffect, watch } from 'vue'
const userId = ref(1)
// watchEffect: tracks userId automatically, runs immediately
watchEffect(async () => {
const data = await fetchUser(userId.value) // userId tracked
user.value = data
})
// watch: explicit source, lazy by default, gives old/new values
watch(userId, async (newId, oldId) => {
const data = await fetchUser(newId)
user.value = data
}, { immediate: true })
Use watchEffect for side effects that depend on multiple reactive sources and don't need old values. Use watch when you need explicit control, old values, or lazy execution.
Composition API
Q11. What is the setup() function? Easy
setup() is the entry point for the Composition API. It runs before component creation, before beforeCreate. It receives props and context (attrs, slots, emit, expose). Everything returned from setup() is available in the template. With the <script setup> syntax sugar, top-level declarations are automatically exposed.
// Options API with setup()
export default {
props: ['title'],
setup(props, { emit, expose }) {
const count = ref(0)
function increment() { count.value++ }
expose({ count }) // expose to parent via template ref
return { count, increment }
}
}
// Script setup (preferred)
<script setup>
const props = defineProps(['title'])
const emit = defineEmits(['update'])
const count = ref(0)
</script>
Q12. What are composables and how do you create one? Medium
Composables are functions that use the Composition API to encapsulate and reuse stateful logic. They follow the use naming convention.
// composables/useCounter.js
import { ref, computed } from 'vue'
export function useCounter(initial = 0) {
const count = ref(initial)
const double = computed(() => count.value * 2)
function increment() { count.value++ }
function decrement() { count.value-- }
function reset() { count.value = initial }
return { count, double, increment, decrement, reset }
}
// composables/useFetch.js
import { ref, watchEffect } from 'vue'
export function useFetch(url) {
const data = ref(null)
const error = ref(null)
const loading = ref(true)
watchEffect(async () => {
loading.value = true
error.value = null
try {
const res = await fetch(url.value ?? url)
data.value = await res.json()
} catch (e) {
error.value = e
} finally {
loading.value = false
}
})
return { data, error, loading }
}
// Usage in component
<script setup>
import { useCounter } from '@/composables/useCounter'
const { count, increment } = useCounter(10)
</script>
Q13. How does provide/inject work in Vue 3? Medium
provide makes a value available to all descendant components without passing it through props. inject retrieves it in a descendant.
// Parent (or App.vue)
import { provide, ref } from 'vue'
const theme = ref('dark')
provide('theme', theme) // provide reactive ref
// Deep child
import { inject } from 'vue'
const theme = inject('theme') // same reactive ref
const theme = inject('theme', 'light') // with default
// Typed injection (recommended)
import { InjectionKey } from 'vue'
const ThemeKey: InjectionKey<Ref<string>> = Symbol('theme')
provide(ThemeKey, theme)
const theme = inject(ThemeKey) // type-safe
Prefer Symbol keys to avoid naming conflicts. For app-wide state, Pinia is more appropriate than provide/inject.
Q14. What are Vue 3 Teleport and Suspense? Medium
Teleport renders a component's template in a different DOM location while keeping it logically inside the component tree.
<Teleport to="body">
<div class="modal" v-if="showModal">
<slot />
<button @click="showModal = false">Close</button>
</div>
</Teleport>
Suspense handles async setup functions and async components, showing fallback content while loading.
<Suspense>
<template #default>
<AsyncComponent />
</template>
<template #fallback>
<LoadingSpinner />
</template>
</Suspense>
AsyncComponent can have async setup() that fetches data; Suspense waits for it to resolve.
Components and Props
Q15. What is the difference between props and emits in Vue 3? Easy
Props are read-only data passed from parent to child. Emits are events the child fires to communicate with the parent. This enforces one-way data flow.
<script setup>
// Child component
const props = defineProps({
modelValue: String, // v-model binding
label: {
type: String,
required: true
},
count: {
type: Number,
default: 0,
validator: (v) => v >= 0
}
})
const emit = defineEmits(['update:modelValue', 'submit'])
function handleInput(e) {
emit('update:modelValue', e.target.value)
}
</script>
Q16. How does v-model work in Vue 3? Medium
v-model on a component is syntactic sugar for binding a prop and listening to an update event.
<CustomInput v-model="text" />
<CustomInput :modelValue="text" @update:modelValue="text = $event" />
<UserForm v-model:first="firstName" v-model:last="lastName" />
// CustomInput.vue
<script setup>
defineProps(['modelValue'])
defineEmits(['update:modelValue'])
</script>
<template>
<input :value="modelValue" @input="$emit('update:modelValue', $event.target.value)" />
</template>
In Vue 2, v-model used value prop and input event. In Vue 3 it uses modelValue and update:modelValue.
Q17. What are slots in Vue and what is the difference between named and scoped slots? Medium
Slots allow parent components to inject template content into a child component.
<template>
<header><slot name="header" /></header>
<main><slot /></main>
<footer><slot name="footer" /></footer>
</template>
<BaseLayout>
<template #header><h1>Title</h1></template>
<p>Main content</p>
<template #footer>Footer text</template>
</BaseLayout>
Scoped slots pass data from child back to parent slot content:
<template>
<ul>
<li v-for="item in items" :key="item.id">
<slot :item="item" :index="index" />
</li>
</ul>
</template>
<DataList :items="users">
<template #default="{ item, index }">
<span>{{ index }}: {{ item.name }}</span>
</template>
</DataList>
Q18. What is the defineExpose macro in script setup? Medium
By default, <script setup> components are closed: nothing is accessible to parent via template ref. defineExpose explicitly exposes selected properties.
// Child.vue
<script setup>
import { ref } from 'vue'
const count = ref(0)
const internalState = ref('private')
defineExpose({ count }) // only count is accessible
</script>
// Parent.vue
<script setup>
import { ref, onMounted } from 'vue'
const childRef = ref(null)
onMounted(() => {
console.log(childRef.value.count) // works
console.log(childRef.value.internalState) // undefined
})
</script>
<template>
<Child ref="childRef" />
</template>
Vue Router
Q19. What are the types of navigation guards in Vue Router 4? Medium
| Guard | Location | Use case |
|---|---|---|
| beforeEach | Global | Auth check, logging |
| afterEach | Global | Analytics, loading stop |
| beforeResolve | Global | Fetch data before render |
| beforeEnter | Per-route | Route-specific auth |
| onBeforeRouteLeave | In-component | Unsaved changes warning |
| onBeforeRouteUpdate | In-component | Same component, param change |
// Global guard
router.beforeEach(async (to, from) => {
if (to.meta.requiresAuth && !isAuthenticated()) {
return { name: 'Login', query: { redirect: to.fullPath } }
}
})
// In-component (Composition API)
import { onBeforeRouteLeave } from 'vue-router'
onBeforeRouteLeave((to, from) => {
if (hasUnsavedChanges.value) {
return confirm('Leave without saving?')
}
})
Q20. What is the difference between router-link and router.push? Easy
<router-link> is a component that renders an <a> tag and integrates with Vue Router. It adds active classes, handles accessibility, and prevents default navigation. It is declarative.
router.push() is programmatic navigation from JavaScript code.
// router-link: declarative
<router-link :to="{ name: 'User', params: { id: userId } }">Profile</router-link>
// programmatic
import { useRouter } from 'vue-router'
const router = useRouter()
async function login() {
await performLogin()
router.push({ name: 'Dashboard' })
}
// replace (no history entry)
router.replace('/home')
// relative navigation
router.push({ path: '../parent' })
Q21. How do you implement dynamic routes and route params? Medium
// router/index.js
const routes = [
{
path: '/users/:id',
name: 'User',
component: UserView,
meta: { requiresAuth: true }
},
{
path: '/posts/:category/:slug',
component: PostView
},
// Catch-all (404)
{
path: '/:pathMatch(.*)*',
name: 'NotFound',
component: NotFoundView
}
]
// In component
import { useRoute } from 'vue-router'
const route = useRoute()
console.log(route.params.id) // dynamic segment
console.log(route.query.page) // query string ?page=2
console.log(route.meta.requiresAuth)
// Watch param changes (same component re-used)
watch(() => route.params.id, (newId) => {
fetchUser(newId)
})
Pinia State Management
Q22. How do you define and use a Pinia store? Easy
// stores/counter.js
import { defineStore } from 'pinia'
import { ref, computed } from 'vue'
// Setup store (Composition API style - preferred)
export const useCounterStore = defineStore('counter', () => {
const count = ref(0)
const double = computed(() => count.value * 2)
function increment() { count.value++ }
function reset() { count.value = 0 }
return { count, double, increment, reset }
})
// Options store (alternative)
export const useCounterStore = defineStore('counter', {
state: () => ({ count: 0 }),
getters: {
double: (state) => state.count * 2
},
actions: {
increment() { this.count++ }
}
})
// In component
<script setup>
import { useCounterStore } from '@/stores/counter'
import { storeToRefs } from 'pinia'
const store = useCounterStore()
const { count, double } = storeToRefs(store) // reactive destructuring
</script>
Q23. What is storeToRefs and why is it needed? Medium
When you destructure a Pinia store directly, reactive properties lose reactivity. storeToRefs wraps each reactive property in a ref so destructured values remain reactive in the template.
const store = useCounterStore()
// WRONG: count is not reactive
const { count, double } = store
// CORRECT: count and double are reactive refs
const { count, double } = storeToRefs(store)
// Actions can be destructured directly (not reactive state)
const { increment, reset } = store
Q24. How does Pinia handle persistence and plugins? Advanced
Pinia supports plugins that can intercept store creation. The popular pinia-plugin-persistedstate handles localStorage persistence.
// Custom plugin: log all actions
function myPlugin({ store }) {
store.$onAction(({ name, args, after, onError }) => {
console.log(`Action: ${name}`, args)
after((result) => console.log(`Result:`, result))
onError((error) => console.error(`Error:`, error))
})
// Subscribe to state changes
store.$subscribe((mutation, state) => {
localStorage.setItem(store.$id, JSON.stringify(state))
})
}
// main.js
const pinia = createPinia()
pinia.use(myPlugin)
// With persisted state plugin
import { createPersistedState } from 'pinia-plugin-persistedstate'
pinia.use(createPersistedState({
storage: localStorage,
serializer: { serialize: JSON.stringify, deserialize: JSON.parse }
}))
Performance and Advanced Patterns
Q25. What is the KeepAlive component and when do you use it? Medium
<KeepAlive> caches component instances when they are toggled off, preserving their state. Lifecycle hooks onActivated and onDeactivated fire instead of mounted/unmounted.
<KeepAlive :include="['UserList']" :max="10">
<component :is="currentTab" />
</KeepAlive>
import { onActivated, onDeactivated } from 'vue'
onActivated(() => {
// refresh data if needed
if (needsRefresh.value) fetchData()
})
onDeactivated(() => {
// pause timers, subscriptions
})
Use when switching between tabs/views that are expensive to remount and benefit from preserved scroll position or form state.
Q26. How do you optimize Vue 3 performance? Advanced
- v-once: render static content once, never update
- v-memo: memoize sub-tree based on a dependency array
- shallowRef / shallowReactive: skip deep reactivity for large data structures
- defineAsyncComponent: code-split heavy components
- Virtual scrolling: for large lists (vue-virtual-scroller)
// v-memo: only update when item.id or selected changes
<div v-for="item in list" :key="item.id" v-memo="[item.id, item.selected]">
{{ item.name }} - {{ expensiveCompute(item) }}
</div>
// Async component with loading state
import { defineAsyncComponent } from 'vue'
const HeavyChart = defineAsyncComponent({
loader: () => import('./HeavyChart.vue'),
loadingComponent: LoadingSpinner,
errorComponent: ErrorDisplay,
delay: 200,
timeout: 3000
})
// shallowRef for large external data
import { shallowRef } from 'vue'
const tableData = shallowRef([]) // Vue tracks only .value change, not nested
Q27. What is the difference between Options API and Composition API? When do you use each? Medium
| Aspect | Options API | Composition API |
|---|---|---|
| Organization | By type (data, methods, computed) | By feature/concern |
| Reuse | Mixins (naming conflicts, unclear source) | Composables (explicit, no conflicts) |
| TypeScript | Limited inference | First-class support |
| Logic sharing | Hard | Easy via composables |
| Learning curve | Lower for beginners | Steeper |
| Code size | More boilerplate for simple cases | More concise for complex cases |
Use Options API for small, simple components or when onboarding developers new to Vue. Use Composition API for large components with multiple concerns, when TypeScript is used, or when building reusable logic.
Q28. How does Vue 3 handle SSR (Server-Side Rendering)? Advanced
Vue 3 supports SSR via @vue/server-renderer or higher-level frameworks like Nuxt 3.
// server.js (Node.js)
import { createSSRApp } from 'vue'
import { renderToString } from '@vue/server-renderer'
const app = createSSRApp({
data: () => ({ count: 1 }),
template: '<button @click="count++">{{ count }}</button>'
})
const html = await renderToString(app)
// Output: <button>1</button>
Vue uses hydration to attach event listeners on the client to server-rendered HTML. Components can use onServerPrefetch to fetch data during SSR. useSSRContext provides the SSR context. Nuxt 3 handles routing, data fetching with useFetch/useAsyncData, and module-level SSR configuration.
Q29. What is the Vue 3 reactivity transform (experimental) and its successor? Advanced
The reactivity transform was a compile-time syntax sugar that allowed writing $ref instead of ref and accessing values without .value. It was deprecated in Vue 3.3 in favor of a more targeted solution.
Vue 3.3+ introduces typed defineProps/defineEmits macros, generic components, and better TypeScript support. The community-accepted alternative for reducing .value boilerplate is the vue-macros library with $ref transforms for those who prefer it, while official Vue recommendation remains standard Composition API syntax.
Q30. Predict the output: Advanced
import { ref, watchEffect, nextTick } from 'vue'
const count = ref(0)
let effectRuns = 0
watchEffect(() => {
effectRuns++
console.log('effect:', count.value)
})
count.value = 1
count.value = 2
await nextTick()
console.log('effectRuns:', effectRuns)
Output:
effect: 0
effect: 2
effectRuns: 2
Explanation: watchEffect runs immediately (effect: 0 is the initial run). The two synchronous assignments to count.value are batched by Vue's scheduler. Only the final value (2) triggers a re-run after the current synchronous code finishes. nextTick waits for the DOM update queue to flush. So effectRuns is 2 (initial + one batched update).
Q31. How do you implement a global error handler in Vue 3? Advanced
// main.js
const app = createApp(App)
app.config.errorHandler = (error, instance, info) => {
console.error('Vue error:', error)
console.log('Component:', instance?.$options.name)
console.log('Info:', info) // e.g., 'setup function'
sendToErrorTracking(error)
}
app.config.warnHandler = (msg, instance, trace) => {
// suppress in tests
if (process.env.NODE_ENV !== 'test') {
console.warn(msg, trace)
}
}
// In-component error boundary
const error = ref(null)
onErrorCaptured((err, instance, info) => {
error.value = err
return false // prevent propagation to parent handlers
})
Q32. What are render functions and h() in Vue 3? Advanced
The h() function (short for hyperscript) creates VNodes directly without templates. Used for programmatic rendering, higher-order components, and cases where templates are limiting.
import { h, ref } from 'vue'
// Functional component as render function
export default {
name: 'MyButton',
props: ['label', 'type'],
setup(props, { slots, emit }) {
const loading = ref(false)
return () => h(
'button',
{
type: props.type || 'button',
class: ['btn', { 'btn-loading': loading.value }],
onClick: () => emit('click')
},
loading.value ? 'Loading...' : slots.default?.() ?? props.label
)
}
}
Additional Topics
Q33. What is Vue's transition system? Medium
The <Transition> component applies CSS or JavaScript animations to entering/leaving elements.
<Transition name="fade" mode="out-in">
<component :is="activeView" :key="activeView" />
</Transition>
<style>
.fade-enter-active, .fade-leave-active { transition: opacity 0.3s; }
.fade-enter-from, .fade-leave-to { opacity: 0; }
</style>
mode="out-in" waits for the leaving transition before the entering transition starts. <TransitionGroup> handles lists with FLIP animations.
Q34. How does Vue 3 handle TypeScript? Medium
Vue 3 is written in TypeScript and provides first-class TS support.
<script setup lang="ts">
import { ref, computed } from 'vue'
interface User {
id: number
name: string
email: string
}
const user = ref<User | null>(null)
const displayName = computed(() => user.value?.name ?? 'Guest')
const props = defineProps<{
title: string
count?: number
}>()
// withDefaults for default values
const propsWithDefaults = withDefaults(defineProps<{
items: string[]
size: 'sm' | 'md' | 'lg'
}>(), {
items: () => [],
size: 'md'
})
const emit = defineEmits<{
update: [value: string]
submit: [payload: User]
}>()
</script>
Q35. What are Vue 3 key best practices for large applications? Advanced
- Folder structure: organize by feature (not by type)
- Auto-import: use
unplugin-auto-importandunplugin-vue-componentsto avoid repetitive imports - Composables: extract all reusable logic; keep components thin
- Pinia stores: one store per domain; use setup stores for complex logic
- Lazy loading:
defineAsyncComponent+ router-level code splitting - Error boundaries:
onErrorCapturedat page level - Testing: Vitest + Vue Test Utils for unit; Playwright for E2E
- Performance: use Vue DevTools profiler, avoid heavy watchers, prefer computed
// router with lazy loading
const routes = [
{
path: '/dashboard',
component: () => import('@/views/Dashboard.vue') // code split
}
]
5-Question Mock Test
Q1. What does storeToRefs do and why is direct destructuring of a Pinia store incorrect?
Q2. Explain the difference between watchEffect and watch. In what scenario would you choose watch over watchEffect?
Q3. How does v-model work on a custom component in Vue 3? Write the child component implementation for <CustomInput v-model="text" />.
Q4. What is the output of the following code?
const count = ref(0)
const double = computed(() => count.value * 2)
count.value = 5
console.log(double.value)
count.value = 10
console.log(double.value)
Q5. What is the difference between ref and reactive? When would you use reactive over ref?
Answers:
A1. storeToRefs wraps each state/getter in a reactive ref, preserving reactivity after destructuring. Direct destructuring extracts plain values that are no longer connected to the store's reactive state.
A2. watchEffect auto-tracks dependencies and is eager (runs immediately). watch requires explicit sources and is lazy. Choose watch when you need the old value, need to react to a specific source only, or want lazy evaluation.
A3. Child needs defineProps(['modelValue']), defineEmits(['update:modelValue']), and :value="modelValue" + @input="$emit('update:modelValue', $event.target.value)".
A4. Output: 10 then 20. Computed updates synchronously when accessed after its dependency changes.
A5. ref works for all types and requires .value. reactive creates a deep reactive object without .value but loses reactivity on destructure. Use reactive when managing a closely related group of state properties that form a single logical object (e.g., form state).
Frequently Asked Questions
What is Vue 3 Composition API and why was it introduced?
The Composition API organizes component logic by feature rather than by Vue option type (data/methods/computed). It was introduced to solve the fragmentation problem in large components where related logic was scattered across multiple options, to replace mixins with conflict-free composables, and to provide better TypeScript inference.
What is Pinia and how does it compare to Vuex?
Pinia is the official Vue state management library. It removes mutations (making Vuex's three-step commit-mutation-state flow unnecessary), supports multiple flat stores without module nesting, has better TypeScript support, and uses a simpler API. Vuex 4 is now in maintenance mode.
How do Vue 3 and React compare for large applications?
Vue 3 offers a more opinionated structure with the Options/Composition API duality, official router and state management (Vue Router, Pinia), and Single File Components. React is more minimal by design, relying on the ecosystem for routing (React Router) and state (Redux, Zustand). Vue's reactivity system is more automatic; React requires explicit dependency arrays. Both are suitable for large applications; choice depends on team familiarity and ecosystem requirements.
What is the internal mesh of related topics?
- React Interview Questions 2026 - compare reactivity models and component patterns
- Next.js Interview Questions 2026 - SSR/SSG with React vs Nuxt
- JavaScript Interview Questions 2026 - Proxy, closures, and async fundamentals
- TypeScript Interview Questions 2026 - typed Vue components and generics
Methodology applied to this articlelast verified 8 Jun 2026
- No fabricated salary numbers or success rates. If we quote a range, it's sourced.
- No noun-substituted templates. This article was not generated by swapping company names in a stock prompt.
- No paid placements, sponsored coaching links, or affiliate-shilled course pushes.
topic cluster
More resources in Interview Questions
Use the category hub to browse similar questions, exam patterns, salary guides, and preparation resources related to this topic.
paid contributor programme
Sat this this year? Share your story, earn ₹500.
First-person experience reports help future candidates prep smarter. We pay verified contributors ₹500 via UPI per accepted story with byline.
Submit your story →ready to practice?
Take a free timed mock test
Put what you learned into practice. Our mock tests match the 2026 pattern with timer, navigator, reveal, and score breakdown. No signup.