Vue Interview Questions and Answers

These are the Vue questions you'll encounter in technical interviews, each with a detailed written answer. Start with reactivity and components, then work through the Composition API, routing, and state management.

40 questions with answersLast updated: Oct 10, 2026
Start a mock interview

What is Vue?

Vue is a progressive JavaScript framework for building user interfaces. It pairs a reactive data model with a component system, and it scales from a single enhanced page to a full single-page application without forcing an all-or-nothing rewrite.

Vue interviews probe how reactivity actually works: refs versus reactive objects, computed properties, watchers, and the lifecycle of a component. You will also be asked to compare the Options API with the Composition API and to justify when each one fits.

Beyond the core, expect questions on component communication, slots, Vue Router, and state management with Pinia. Understanding how Vue tracks dependencies is what separates a confident answer from a memorised one.

Reading answers is not the same as giving them. Sit a full mock interview and get scored on how you actually explain them.

Try a mock interview

Vue Interview Questions and Answers

1Multiple choice

How would you describe Vue.js and its primary purpose in web development?

Answer

Vue.js is a JavaScript framework for building UIs using components, declarative templates, reactive state, and directives.

2Multiple choice

What is the .vue file format and what does it enable?

Answer

Single-File Components (.vue) keep a component's template, script, and styles together, improving organization in build-tool projects.

3Multiple choice

How does the Composition API differ in structure from other Vue API styles?

Answer

The Composition API uses functions like ref(), reactive(), computed(), and watch() to organize component logic.

4Multiple choice

When would you choose v-if over v-show for conditional rendering?

Answer

v-if unmounts elements from the DOM, reducing render cost initially. Use it when conditions change rarely.

5Multiple choice

What role do keys play when rendering lists that can change?

Answer

Stable keys help Vue maintain component state across list changes. Index keys can cause state to persist on wrong rows.

6Multiple choice

Describe the flow of data when using props between parent and child components

Answer

Props pass data from parent to child. Children should emit events to request changes, not mutate props.

7Multiple choice

How does v-model create a two-way binding convention in Vue components?

Answer

v-model on a component is a convention using a prop and update event, useful for form-like components.

8Multiple choice

Explain how slots enable content composition in Vue components

Answer

Slots allow parents to pass template content to children. Useful for layout, cards, modals, and reusable shells.

9Multiple choice

When should you use provide/inject versus props for passing data?

Answer

Use provide/inject for shared context like theme or form state. Use props for direct parent-child communication.

10Multiple choice

How do composables package and reuse stateful logic in Vue?

Answer

Composables are functions combining Vue API features like ref, watch, and lifecycle hooks for reusable logic.

11Written answer

Explain Vue.js fundamentals including its component model and key architectural concepts

Answer

Vue.js is a JavaScript framework for building user interfaces using components, which are reusable, independent, and encapsulated pieces of UI. Each component has its own template (HTML), script (logic), and styles. Vue provides reactivity through a dependency-tracking system that updates components when state changes. The framework supports progressive adoption, allowing use on part of a page or as a full application. Key features include declarative templates, directives, component lifecycle, and an ecosystem for routing, state management, and server rendering.

12Written answer

Describe the structure and benefits of Single-File Components in build-tool-enabled projects

Answer

Single-File Components (.vue files) contain a component's template, script, and styles in one file. The structure separates concerns into three sections: template for markup, script for logic, and optional style for scoped CSS. Benefits include keeping related code together, improving maintainability, enabling scoped styling to prevent conflicts, supporting modern tooling like TypeScript and preprocessors, and making components portable and reusable. SFCs are the standard approach in modern Vue projects with build steps.

13Written answer

Compare the Composition API approach with the Options API, discussing tradeoffs for different scenarios

Answer

The Options API organizes logic into options like data, methods, computed, watch, and lifecycle hooks. It's approachable for simpler components and familiar to developers from other frameworks. The Composition API uses imported functions like ref, reactive, computed, and watch to write logic in any order. It scales better for complex components, provides better code organization for related logic, and enables extraction into reusable composables. Neither is universally better; Options API suits straightforward components while Composition API excels with shared logic and complexity.

14Written answer

Explain how ref() works and when you would use it for reactive values

Answer

ref() creates a reactive wrapper around a value. In templates, refs are automatically unwrapped; in scripts, access the value via .value property. Use ref() for primitive values (strings, numbers, booleans) and when a value might be replaced entirely. Unlike reactive(), which wraps objects, ref() works for any type and supports complete reassignment. Example: const count = ref(0); count.value += 1 updates the reactive value.

15Written answer

Describe reactive() and explain the pitfall when destructuring reactive objects

Answer

reactive() creates a reactive proxy for objects. Modifications to object properties trigger reactivity. The pitfall occurs when destructuring because destructuring breaks reactivity: const { email } = form gives you the current value but not a reactive binding. To preserve reactivity when destructuring, use toRefs(): const { email } = toRefs(form) creates reactive bindings. This is a common source of 'why isn't my template updating?' bugs.

16Written answer

Distinguish between computed and watch, explaining when to use each

Answer

computed() creates derived values calculated from reactive state, used during render. It's ideal for formatting, filtering, or transforming data. computed() is cached and only recalculates when dependencies change. watch() handles side effects when reactive values change, like API calls, storage sync, logging, or bridging to non-Vue code. Use computed when you can calculate during render; use watch for side effects that shouldn't run every render. If you can calculate it, use computed.

17Written answer

Explain Vue's dependency tracking system and how reactivity updates components

Answer

Vue tracks reactive reads during rendering and effect execution, recording which values were accessed. When tracked reactive values change, Vue updates the affected parts without rerendering everything. Vue 3 uses JavaScript proxies for object reactivity and ref wrappers for individual values. The system automatically establishes dependencies without manual declaration. This fine-grained reactivity means only components that depend on changed data update, not the entire app.

18Written answer

Define composables and discuss how they encapsulate reusable stateful logic

Answer

Composables are functions combining Composition API features to package reusable stateful logic. They use ref, reactive, computed, watch, and lifecycle hooks to create self-contained, portable units of functionality. Good composables clean up side effects in onUnmounted, have small APIs, and handle their own state. Example: useWindowWidth() manages window resize listeners and exposes a reactive width value. Composables replace mixins for better code reuse with clearer dependencies.

19Written answer

Describe the pattern for managing loading, success, error, and empty states when fetching data

Answer

Separate request state into distinct properties: loading (boolean), data (result when successful), error (error message), and empty (no results). Fetch data when routes load or inputs change. Model the flow: initial loading state, transition to success/error/empty based on result. Always show one state to users: loading spinner, data, error message with retry option, or empty state message. In SSR or framework setups, prefer the framework's data-loading pattern over component-level fetching.

20Written answer

Explain which state should go in the URL versus stay as local component state

Answer

Put shareable, bookmarkable state in the URL: search queries, filters, sort order, page numbers, selected tabs where back-button behavior is expected. Keep ephemeral UI state local: open menus, draft form input, hover state, temporary loading flags. This distinction lets users bookmark and share URLs representing the exact view they see. Local state avoids polluting the URL bar and stays easier to reason about since only the component owns it.

21Spoken answer

Walk through how you would design a reusable modal component that other developers can use across the application

This browser cannot record audio. Answer the question out loud anyway, then compare yourself against the model answer below.

Answer

I'd use props for the open state, emits for close and confirm events, and slots for content. For accessibility, I'd add focus trapping using onMounted and onUnmounted lifecycle hooks to manage focus on open/close. I'd handle Escape key dismissal, lock scroll when modal opens, and set proper ARIA attributes like role='dialog' and aria-label. I'd test keyboard navigation to ensure it's fully accessible. The component should clean up all event listeners in onUnmounted to prevent memory leaks.

22Spoken answer

Describe a situation where you chose to use provide/inject instead of props and explain why it was the right choice

This browser cannot record audio. Answer the question out loud anyway, then compare yourself against the model answer below.

Answer

I used provide/inject when building a form with many nested field components. Passing props through every level created prop drilling that made changes difficult. By providing form state and validation functions at the top, child fields accessed what they needed without intermediate components caring. It was cleaner and changes to form logic didn't ripple through the tree. Props would still be used for direct parent-child communication; provide/inject solved the deep nesting problem.

23Spoken answer

Explain how you would debug a component that isn't updating when its data changes, even though you believe it should be reactive

This browser cannot record audio. Answer the question out loud anyway, then compare yourself against the model answer below.

Answer

I'd first check if the value is actually reactive by adding a console.log and verifying it changes. Then I'd check for common reactivity breaking patterns: destructuring without toRefs, reassigning ref.value outside reactive scope, or mutating nested object properties. I might be using v-show when I meant v-if, or the dependency might not be declared in computed or watch. I'd also verify the component is actually bound to the data, not showing stale values in the template.

24Spoken answer

Talk about a time you optimized a Vue component or list that was rendering slowly and what techniques you applied

This browser cannot record audio. Answer the question out loud anyway, then compare yourself against the model answer below.

Answer

I profiled the component and found rendering thousands of rows in a list was the bottleneck. I implemented virtual scrolling to render only visible rows, reducing DOM nodes dramatically. I also moved expensive calculations to computed properties instead of template expressions. I added stable keys to prevent state loss when filtering. For large immutable datasets, I used shallowRef to avoid deep reactivity overhead. These changes made the list interactive even with large datasets.

25Spoken answer

Describe how you would approach organizing components, composables, and stores in a large Vue application to keep it maintainable

This browser cannot record audio. Answer the question out loud anyway, then compare yourself against the model answer below.

Answer

I organize by feature when features are substantial: each feature gets a folder with route, components, composables, store (if needed), and API client. Shared UI lives in a shared components folder. Utilities and helpers go in a common folder. This structure scales; features can be developed independently. I avoid a monolithic components folder with hundreds of one-offs. Types go alongside code that uses them. Each section is clear about what it owns.

26Spoken answer

Explain how you would handle form validation in Vue, including display of error messages and preventing submission when invalid

This browser cannot record audio. Answer the question out loud anyway, then compare yourself against the model answer below.

Answer

Discuss your approach and the tradeoffs you considered

27Spoken answer

Walk through how you would implement a feature where URL filters sync with component state so users can bookmark results

This browser cannot record audio. Answer the question out loud anyway, then compare yourself against the model answer below.

Answer

Discuss your approach and the tradeoffs you considered

28Spoken answer

Describe how you would set up a composable that handles loading, error, and retry logic for API requests

This browser cannot record audio. Answer the question out loud anyway, then compare yourself against the model answer below.

Answer

Discuss your approach and the tradeoffs you considered

29Spoken answer

Explain how you would teach a junior developer the difference between Composition API and Options API to help them choose the right approach

This browser cannot record audio. Answer the question out loud anyway, then compare yourself against the model answer below.

Answer

Discuss your approach and the tradeoffs you considered

30Spoken answer

Talk about managing component communication in a scenario where deeply nested children need to notify distant ancestors about state changes

This browser cannot record audio. Answer the question out loud anyway, then compare yourself against the model answer below.

Answer

Discuss your approach and the tradeoffs you considered

31Coding

Create a Vue component that displays a counter with increment and decrement buttons using the Composition API

<template>
  <div class="counter">
    <!-- Display counter value and add buttons -->
  </div>
</template>

<script setup lang="ts">
// Implement counter logic here
</script>

Answer

<template>
  <div class="counter">
    <p>Count: {{ count }}</p>
    <button @click="increment">Increment</button>
    <button @click="decrement">Decrement</button>
  </div>
</template>

<script setup lang="ts">
import { ref } from 'vue';

const count = ref(0);

function increment() {
  count.value++;
}

function decrement() {
  count.value--;
}
</script>
32Coding

Create a component that displays a list of products and filters them based on a search input

<template>
  <div class="products">
    <input v-model="searchTerm" placeholder="Search products..." />
    <!-- Render filtered products -->
  </div>
</template>

<script setup lang="ts">
import { ref, computed } from 'vue';

const searchTerm = ref('');
const products = [
  { id: 1, name: 'Laptop' },
  { id: 2, name: 'Mouse' },
  { id: 3, name: 'Keyboard' }
];
// Add filtering logic here
</script>

Answer

<template>
  <div class="products">
    <input v-model="searchTerm" placeholder="Search products..." />
    <ul>
      <li v-for="product in filteredProducts" :key="product.id">
        {{ product.name }}
      </li>
    </ul>
  </div>
</template>

<script setup lang="ts">
import { ref, computed } from 'vue';

const searchTerm = ref('');
const products = [
  { id: 1, name: 'Laptop' },
  { id: 2, name: 'Mouse' },
  { id: 3, name: 'Keyboard' }
];

const filteredProducts = computed(() =>
  products.filter(p =>
    p.name.toLowerCase().includes(searchTerm.value.toLowerCase())
  )
);
</script>
33Coding

Build a component that toggles the visibility of details content when a button is clicked using v-if

<template>
  <div class="details">
    <button @click="toggleDetails">Show Details</button>
    <!-- Conditionally render details -->
  </div>
</template>

<script setup lang="ts">
import { ref } from 'vue';
// Add logic here
</script>

Answer

<template>
  <div class="details">
    <button @click="toggleDetails">
      {{ showDetails ? 'Hide Details' : 'Show Details' }}
    </button>
    <div v-if="showDetails" class="content">
      <p>This is additional information about the item.</p>
      <p>It appears and disappears from the DOM.</p>
    </div>
  </div>
</template>

<script setup lang="ts">
import { ref } from 'vue';

const showDetails = ref(false);

function toggleDetails() {
  showDetails.value = !showDetails.value;
}
</script>
34Coding

Create a dropdown component that displays a list of menu options when clicked and emits the selected option

<template>
  <div class="dropdown">
    <button @click="toggleMenu">{{ selectedOption || 'Select an option' }}</button>
    <!-- Render menu items when open -->
  </div>
</template>

<script setup lang="ts">
import { ref } from 'vue';

const isOpen = ref(false);
const selectedOption = ref(null);
const options = ['Option A', 'Option B', 'Option C'];

const emit = defineEmits<{
  select: [option: string];
}>();
// Add logic here
</script>

Answer

<template>
  <div class="dropdown">
    <button @click="toggleMenu">{{ selectedOption || 'Select an option' }}</button>
    <ul v-if="isOpen" class="menu">
      <li v-for="option in options" :key="option" @click="selectOption(option)">
        {{ option }}
      </li>
    </ul>
  </div>
</template>

<script setup lang="ts">
import { ref } from 'vue';

const isOpen = ref(false);
const selectedOption = ref(null);
const options = ['Option A', 'Option B', 'Option C'];

const emit = defineEmits<{
  select: [option: string];
}>();

function toggleMenu() {
  isOpen.value = !isOpen.value;
}

function selectOption(option: string) {
  selectedOption.value = option;
  isOpen.value = false;
  emit('select', option);
}
</script>
35Coding

Create a component that displays items with prices and uses a computed property to calculate the total

<template>
  <div class="shopping">
    <ul>
      <!-- Display items with prices -->
    </ul>
    <p>Total: {{ total }}</p>
  </div>
</template>

<script setup lang="ts">
import { ref, computed } from 'vue';

const items = ref([
  { name: 'Book', price: 15 },
  { name: 'Pen', price: 2 },
  { name: 'Notebook', price: 8 }
]);
// Calculate total with computed
</script>

Answer

<template>
  <div class="shopping">
    <ul>
      <li v-for="item in items" :key="item.name">
        {{ item.name }}: ${{ item.price }}
      </li>
    </ul>
    <p>Total: ${{ total }}</p>
  </div>
</template>

<script setup lang="ts">
import { ref, computed } from 'vue';

const items = ref([
  { name: 'Book', price: 15 },
  { name: 'Pen', price: 2 },
  { name: 'Notebook', price: 8 }
]);

const total = computed(() =>
  items.value.reduce((sum, item) => sum + item.price, 0)
);
</script>
36Coding

Build a component with an email input that validates the format and displays error messages

<template>
  <div class="email-form">
    <input v-model="email" type="email" placeholder="Enter email" />
    <!-- Show validation error if needed -->
  </div>
</template>

<script setup lang="ts">
import { ref, computed } from 'vue';

const email = ref('');
// Add validation logic
</script>

Answer

<template>
  <div class="email-form">
    <input v-model="email" type="email" placeholder="Enter email" />
    <p v-if="emailError" class="error">{{ emailError }}</p>
  </div>
</template>

<script setup lang="ts">
import { ref, computed } from 'vue';

const email = ref('');

const emailError = computed(() => {
  if (!email.value) return '';
  const regex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
  return regex.test(email.value) ? '' : 'Invalid email format';
});
</script>
37Coding

Create a component that toggles a CSS class on an element based on a button click

<template>
  <div>
    <button @click="toggleActive">Toggle Style</button>
    <!-- Apply active class conditionally -->
  </div>
</template>

<script setup lang="ts">
import { ref } from 'vue';
// Add logic here
</script>

<style scoped>
.active {
  background-color: green;
  color: white;
}
</style>

Answer

<template>
  <div>
    <button @click="toggleActive">Toggle Style</button>
    <div :class="{ active: isActive }" class="box">
      Styled Content
    </div>
  </div>
</template>

<script setup lang="ts">
import { ref } from 'vue';

const isActive = ref(false);

function toggleActive() {
  isActive.value = !isActive.value;
}
</script>

<style scoped>
.box {
  padding: 20px;
  background-color: gray;
}

.active {
  background-color: green;
  color: white;
}
</style>
38Coding

Create a component that allows adding new todos and marking them as completed

<template>
  <div class="todos">
    <input v-model="newTodo" @keyup.enter="addTodo" placeholder="Add a todo" />
    <!-- Display todos with completion toggle -->
  </div>
</template>

<script setup lang="ts">
import { ref } from 'vue';
// Add todo logic here
</script>

Answer

<template>
  <div class="todos">
    <input v-model="newTodo" @keyup.enter="addTodo" placeholder="Add a todo" />
    <ul>
      <li v-for="todo in todos" :key="todo.id" :class="{ completed: todo.done }">
        <input type="checkbox" v-model="todo.done" />
        {{ todo.text }}
      </li>
    </ul>
  </div>
</template>

<script setup lang="ts">
import { ref } from 'vue';

const newTodo = ref('');
const todos = ref([]);
let todoId = 0;

function addTodo() {
  if (newTodo.value.trim()) {
    todos.value.push({ id: ++todoId, text: newTodo.value, done: false });
    newTodo.value = '';
  }
}
</script>
39Coding

Create a component that displays product prices formatted as currency using a computed property

<template>
  <div class="inventory">
    <ul>
      <!-- Display products with formatted prices -->
    </ul>
  </div>
</template>

<script setup lang="ts">
import { computed, ref } from 'vue';

const products = ref([
  { id: 1, name: 'Widget', price: 19.99 },
  { id: 2, name: 'Gadget', price: 49.50 }
]);
// Add formatting logic
</script>

Answer

<template>
  <div class="inventory">
    <ul>
      <li v-for="product in products" :key="product.id">
        {{ product.name }}: {{ formattedPrice(product.price) }}
      </li>
    </ul>
  </div>
</template>

<script setup lang="ts">
import { ref } from 'vue';

const products = ref([
  { id: 1, name: 'Widget', price: 19.99 },
  { id: 2, name: 'Gadget', price: 49.50 }
]);

function formattedPrice(price: number): string {
  return new Intl.NumberFormat('en-US', {
    style: 'currency',
    currency: 'USD'
  }).format(price);
}
</script>
40Coding

Build a button component that emits a custom event with data when clicked

<template>
  <button @click="handleClick">
    Click Me
  </button>
</template>

<script setup lang="ts">
const emit = defineEmits<{
  myEvent: [message: string];
}>();

function handleClick() {
  // Emit event with data
}
</script>

Answer

<template>
  <button @click="handleClick">
    Click Me
  </button>
</template>

<script setup lang="ts">
const emit = defineEmits<{
  myEvent: [message: string];
}>();

function handleClick() {
  emit('myEvent', 'Button was clicked!');
}
</script>