Interview Q&A · 59 questions · 22 answered publicly

Vue.js Developer Interview Questions

Ordered by how often they're asked. Sign in free to see every answer, rate your confidence and run timed mock interviews.

  1. Most asked Options API or Composition API: what is the difference, and which would you use?

    Summary

    The Options API organises a component by option type (data, computed, methods, watch); the Composition API organises it by logical concern using functions (ref, computed, watch) inside setup() or <script setup>. Both are fully supported in Vue 3; the Composition API scales better for complex components, reuse (composables) and TypeScript.

    Longer Answer

    In a large Options API component, the code for one feature (say, search) is spread across data, computed, watch and methods; with the Composition API it sits together and can be extracted into a composable such as useSearch(). The Composition API also gives better type inference and avoids this.

    The Options API is still fine for simple components and for teams coming from Vue 2. Mixing styles in one codebase works but is harder to maintain; most new Vue 3 projects use <script setup> with the Composition API.

    Key Points

    • Options: grouped by option type.
    • Composition: grouped by concern.
    • Composables for reuse.
    • Better TypeScript with Composition.

    Permalink

  2. Most asked ref or reactive: what is the difference, and when do you use each?

    Summary

    ref() wraps any value (primitives included) in an object with a .value property; reactive() makes an object deeply reactive through a Proxy and only works with objects. The Vue docs recommend ref() as the primary API because reactive() has limitations.

    Longer Answer

    reactive() limitations: it cannot hold primitives; replacing the whole object breaks the reactive connection (state = reactive({...}) again loses tracking for existing references); and destructuring or passing a property to a function loses reactivity (const { count } = state is a plain number). ref avoids these: you can reassign .value freely. Refs are automatically unwrapped in templates and when nested inside reactive objects (but not inside arrays or Maps).

    toRefs(state) converts a reactive object's properties into refs so destructuring keeps reactivity, a common pattern when returning state from composables.

    Code Example

    import { ref, reactive, toRefs } from "vue";
    
    const count = ref(0);
    count.value++;            // template: {{ count }} (auto-unwrapped)
    
    const state = reactive({ user: null, loading: false });
    const { loading } = toRefs(state);  // stays reactive
    

    Key Points

    • ref: any value, .value.
    • reactive: objects only, Proxy-based.
    • Destructuring reactive loses reactivity.
    • Docs recommend ref by default; toRefs for destructuring.

    Permalink

  3. Most asked How does Vue 3's reactivity system work, and how does it differ from Vue 2's?

    Summary

    Vue 3 wraps objects in JavaScript Proxies: reading a property inside an effect (a component render, computed, watchEffect) records a dependency (track), and writing the property re-runs the effects that depend on it (trigger). Vue 2 used Object.defineProperty getters and setters, which could not detect added or deleted properties or array index assignments.

    Longer Answer

    In Vue 2 you needed Vue.set(obj, 'newKey', value) and array mutation methods because obj.newKey = 1 and arr[0] = x were invisible to reactivity. Proxies intercept all of these, plus Map, Set, in and delete, so Vue 3 removed Vue.set/Vue.delete.

    Dependencies are tracked per property, so updates are fine-grained: changing user.name only re-renders components that read user.name. Effects are batched and applied asynchronously before the next paint, which is why nextTick() is needed to read the updated DOM.

    Key Points

    • Proxies: track on read, trigger on write.
    • Vue 2 defineProperty missed new keys/array indexes.
    • Fine-grained per-property dependencies.
    • Batched async updates; nextTick.

    Permalink

  4. Most asked computed, watch or watchEffect: when do you use each?

    Summary

    computed derives a value from reactive state and caches it until its dependencies change; use it for anything you would display or pass on. watch runs a side effect when specific sources change, with access to old and new values. watchEffect runs a side effect immediately and re-runs whenever any reactive value it read changes.

    Longer Answer

    Rules of thumb:

    • Deriving data: computed, never a watcher that writes to another ref.
    • Side effects (fetching, writing to localStorage, calling an API) in response to changes: watch with explicit sources (clear intent, lazy by default, immediate / deep / once options).
    • Quick effects whose dependencies are obvious from the code: watchEffect.

    Computed getters must be pure. Watchers that fetch data should handle races: onWatcherCleanup (3.5+) or the onCleanup argument lets you abort the previous request when the source changes again.

    Code Example

    const fullName = computed(() => `${first.value} ${last.value}`);
    
    watch(query, async (q, _old, onCleanup) => {
      const controller = new AbortController();
      onCleanup(() => controller.abort());
      results.value = await search(q, { signal: controller.signal });
    });
    

    Key Points

    • computed: cached derived values, pure.
    • watch: explicit sources, old/new values.
    • watchEffect: auto-tracked, runs immediately.
    • Clean up async work in watchers.

    Permalink

  5. Most asked Why does v-for need a key, and why should it not be the index?

    Summary

    The key lets Vue match list items to existing DOM elements and component instances when the list changes, so it can move them instead of patching in place. A stable unique ID keeps each item's state (inputs, focus, component state, transitions) attached to the right data; the array index changes when items are inserted, removed or reordered, so state ends up on the wrong item.

    Longer Answer

    Without keys (or with index keys), Vue reuses elements by position. Deleting the first item of a list of inputs then shows the typed text shifted onto the wrong rows. Index keys are only safe for static lists that never reorder and have no per-item state. Keys must be unique among siblings and be strings or numbers.

    Key Points

    • Keys identify items across renders.
    • Use a stable unique ID.
    • Index keys misplace state on reorder.
    • Required for transitions and component lists.

    Deep dive: Code Clinic: review a Vue comment list

    Permalink

  6. Most asked How do props work, and what is one-way data flow?

    Summary

    Props pass data from parent to child; when the parent's value changes, the child receives the update. The child must not mutate a prop: data flows down, and changes go back up as events, so the parent stays the owner of its state.

    Longer Answer

    Declare props with defineProps (with types and defaults, or TypeScript generics). If the child needs a local, editable copy, initialise a ref from the prop; if it needs a transformed value, use a computed. Mutating a primitive prop triggers a warning; mutating a property of an object prop does not warn (objects are shared by reference) but is still bad practice, because the parent's state changes behind its back.

    Since Vue 3.5, destructuring defineProps() keeps reactivity in <script setup> and supports default values with plain JavaScript defaults.

    Key Points

    • Parent to child, read-only in the child.
    • Changes go up via events.
    • Local copy (ref) or derived value (computed).
    • Object props are shared references.

    Deep dive: Code Clinic: review a Vue comment list

    Permalink

  7. Most asked What are composables, and why did they replace mixins?

    Summary

    Composables are functions that use the Composition API to encapsulate and reuse stateful logic, such as useFetch, useMouse or usePagination. They return refs and functions explicitly. Mixins merged options into components implicitly, which caused unclear property origins, name collisions and hidden coupling.

    Longer Answer

    With a composable you can see where every value comes from (const { data, error } = useFetch(url)), rename on destructuring, and pass arguments. Conventions: name them useX, accept refs or getters as inputs (toValue() normalises them), clean up side effects with onUnmounted or onScopeDispose, and return plain objects of refs. VueUse is a large collection of ready-made composables.

    Code Example

    import { ref, onMounted, onUnmounted } from "vue";
    
    export function useWindowWidth() {
      const width = ref(window.innerWidth);
      const update = () => (width.value = window.innerWidth);
      onMounted(() => window.addEventListener("resize", update));
      onUnmounted(() => window.removeEventListener("resize", update));
      return { width };
    }
    

    Key Points

    • Functions encapsulating stateful logic.
    • Explicit inputs and outputs.
    • Mixins had collisions and hidden origins.
    • VueUse for common needs.

    Permalink

  8. Most asked Pinia or Vuex: what is the difference, and why is Pinia now recommended?

    Summary

    Pinia is the official state management library for Vue 3 (Vuex is in maintenance mode). It drops mutations (actions can change state directly), has a simpler API, first-class TypeScript inference, modular stores by default instead of nested modules, and Devtools support including time travel.

    Longer Answer

    A Pinia store can be written in options style (state, getters, actions) or setup style (a function using refs and computed, like a composable). Stores are created lazily when first used, which works well with code splitting. For SSR, state is per request (no module-level singletons leaking between users) and serialised for hydration.

    Not everything belongs in a store: keep local UI state in components and server data in a data-fetching layer when possible.

    Key Points

    • Official store for Vue 3; Vuex in maintenance.
    • No mutations; simpler API.
    • Excellent TypeScript support.
    • Options or setup style stores.

    Permalink

  9. Most asked How do you use Vue Router: dynamic routes, nested routes and navigation guards?

    Summary

    Routes map paths to components; dynamic segments (/users/:id) become route.params; nested routes render children inside a parent's <RouterView>; navigation guards (router.beforeEach, per-route beforeEnter, in-component onBeforeRouteLeave) can redirect or cancel navigation, for example for authentication or unsaved changes.

    Longer Answer

    router.beforeEach((to) => {
      const auth = useAuthStore();
      if (to.meta.requiresAuth && !auth.isLoggedIn) {
        return { name: "login", query: { redirect: to.fullPath } };
      }
    });
    

    In Vue Router 4, guards return false to cancel, a route location to redirect, or nothing to continue; the next callback is optional. When only params change (/users/1 to /users/2) the same component instance is reused, so watch route.params (or use a key on <RouterView>) to reload data. Client-side guards are for user experience; the server must still authorise every API call.

    Key Points

    • Dynamic params, nested routes.
    • Guards return false / location / nothing.
    • Same component reused on param change.
    • Guards are UX; the API must authorise.

    Deep dive: Request Lifecycle course: The Browser Side: React and Vue Page Loads

    Permalink

  10. Most asked How do you optimise the performance of a Vue application?

    Summary

    Measure first (Vue Devtools performance timeline, browser profiler, Lighthouse), then: reduce bundle size (lazy routes, tree-shaking, analysing dependencies), avoid unnecessary re-renders (stable props, computed values, v-once / v-memo for static or rarely changing subtrees), virtualise long lists, use shallowRef for large immutable data, and server-render or pre-render pages that need fast first paint.

    Longer Answer

    Common culprits: rendering thousands of rows (use a virtual scroller), passing new object or array literals as props on every render (child re-renders), deep watchers on large objects, making huge third-party objects deeply reactive (wrap with markRaw or shallowRef), and heavy work in computed properties that depend on frequently changing state.

    Vue's compiler already optimises templates (static hoisting, patch flags), so focus on data size, reactivity scope and component granularity rather than micro-memoisation.

    Key Points

    • Measure first.
    • Lazy loading and bundle analysis.
    • Virtual lists; shallowRef/markRaw for big data.
    • v-once / v-memo; stable props.

    Permalink

  11. Most asked What security risks should Vue developers watch for?

    Summary

    Vue escapes text interpolation ({{ }}) and attribute bindings, so the main XSS risks are what bypasses that: v-html with untrusted content, user-controlled URLs in href/src (javascript: URLs), user-provided templates compiled at runtime, and server-side templates that interpolate user data into the HTML that Vue then mounts on.

    Longer Answer

    Rules from the Vue security guide: never use non-trusted content as a component template; sanitise HTML with DOMPurify before v-html (or render Markdown to sanitised HTML on the server); validate URLs (allow only http:/https:); do not bind user-controlled style objects without care; and use a Content Security Policy. Client-side checks (route guards, hidden buttons) never replace server-side authorisation.

    Key Points

    • v-html with untrusted content is XSS.
    • Validate URLs; no user templates.
    • Sanitise with DOMPurify; use CSP.
    • Authorise on the server.

    Deep dive: Code Clinic: review a Vue comment list

    Permalink

  12. Most asked Describe the Vue component lifecycle hooks and what you use them for.

    Summary

    Composition API hooks: onBeforeMount, onMounted (DOM available; start DOM-dependent work), onBeforeUpdate / onUpdated, onBeforeUnmount / onUnmounted (clean up timers, listeners, subscriptions), plus onErrorCaptured, and onActivated / onDeactivated for KeepAlive. setup() itself replaces Vue 2's beforeCreate and created.

    Longer Answer

    Common patterns: fetch data in setup (or onMounted when it depends on the DOM, or with Suspense/Nuxt's data fetching for SSR); add global event listeners in onMounted and remove them in onUnmounted; avoid changing state in onUpdated, which can loop.

    During server-side rendering only setup runs (plus onServerPrefetch); onMounted and later hooks run only in the browser, so browser-only code (window, localStorage) belongs there. Hooks must be registered synchronously during setup.

    Key Points

    • setup replaces beforeCreate/created.
    • onMounted for DOM work; onUnmounted for cleanup.
    • Only setup runs on the server.
    • Register hooks synchronously.

    Permalink

  13. Most asked v-if or v-show: what is the difference?

    Summary

    v-if adds and removes the element (and its component instances) from the DOM; v-show always renders it and toggles CSS display. v-if has a higher toggle cost but nothing is rendered when false; v-show has a higher initial cost but cheap toggling.

    Longer Answer

    Use v-show for things toggled often (tabs, dropdowns) and v-if for conditions that rarely change or when the hidden content is expensive or should not exist (permissions, lazy sections). v-if also resets the component's state each time it is shown, which can be a feature or a bug. v-show does not work on <template> and has no v-else.

    Key Points

    • v-if: conditional rendering.
    • v-show: CSS display toggle.
    • Frequent toggles: v-show.
    • v-if resets component state.

    Permalink

  14. Most asked How does v-model work on components, and what does defineModel do?

    Summary

    On a component, v-model is shorthand for passing a modelValue prop and listening for update:modelValue. defineModel() (stable since Vue 3.4) declares that pair in one line and returns a ref you can read and assign, which emits the update automatically.

    Longer Answer

    <!-- Child: QuantityInput.vue -->
    <script setup>
    const qty = defineModel({ type: Number, default: 1 });
    </script>
    <template><input type="number" v-model="qty" min="1" /></template>
    
    <!-- Parent -->
    <QuantityInput v-model="cart.qty" />
    

    Components can have several named models (v-model:title, v-model:open) and custom modifiers (v-model.trim). Before 3.4 you wrote the prop, the emit and a computed getter/setter by hand. Vue 3 replaced Vue 2's value/input convention and .sync with this scheme.

    Key Points

    • modelValue prop + update:modelValue event.
    • defineModel (3.4+) simplifies it.
    • Multiple named v-models.
    • Replaces Vue 2's value/input and .sync.

    Permalink

  15. Most asked Explain default, named and scoped slots.

    Summary

    Slots let a parent pass template content into a child. The default slot receives untargeted content; named slots (<template #header>) target specific outlets; scoped slots let the child pass data back to the slot content (<slot :item="item"> used as <template #row="{ item }">).

    Longer Answer

    Scoped slots power flexible, reusable components: a data table renders rows but lets the parent decide how each cell looks; a list component handles fetching and pagination and exposes items through the slot. Renderless components take this to the extreme (all logic, no markup), although composables now cover most of those use cases. $slots / useSlots() let a component check whether a slot was provided.

    Key Points

    • Default, named (#name), scoped (slot props).
    • Child passes data to slot content.
    • Enables flexible, reusable components.
    • Composables often replace renderless components.

    Permalink

  16. Most asked What are template refs, and why do you need nextTick?

    Summary

    A template ref gives direct access to a DOM element or child component instance (ref="input" plus const input = ref(null), or useTemplateRef('input') in 3.5+), available after mount. nextTick() waits until Vue has applied pending reactive updates to the DOM, because Vue batches updates asynchronously.

    Longer Answer

    show.value = true;      // the element is not in the DOM yet
    await nextTick();       // DOM updated
    input.value.focus();
    

    Use template refs for focus management, measuring elements, and integrating non-Vue libraries (charts, maps). Prefer declarative state over manipulating the DOM directly.

    Key Points

    • Refs to DOM nodes or component instances.
    • Available after mount.
    • Updates are batched; nextTick waits for the DOM.
    • useTemplateRef in 3.5+.

    Permalink

  17. Most asked Why does destructuring sometimes lose reactivity in Vue, and how do you avoid it?

    Summary

    Reactivity is tracked through property access on a proxy or .value on a ref. Destructuring a reactive() object, a store, or (before Vue 3.5) props copies the current values into plain variables, which no longer update.

    Longer Answer

    Fixes: keep the object and access properties (state.count), use toRefs() / toRef() to get refs per property, storeToRefs() for Pinia stores, and in Vue 3.5+ props destructuring in <script setup> is compiled to keep reactivity. When passing a reactive value into a composable, pass a ref or getter (() => props.id) rather than the raw value, and read it with toValue().

    Key Points

    • Destructuring copies values.
    • toRefs / toRef / storeToRefs.
    • Props destructure reactive in 3.5+.
    • Pass refs or getters to composables.

    Permalink

  18. Most asked What are the main breaking changes when migrating from Vue 2 to Vue 3?

    Summary

    The app is created with createApp() instead of new Vue(); global APIs moved to the app instance; v-model on components uses modelValue/update:modelValue (and .sync was removed); filters, $on/$off event buses, $listeners and $children were removed; v-if now has priority over v-for; functional and async component syntax changed; and Vue Router 4, Pinia (instead of Vuex 3) and Vite are the standard stack.

    Longer Answer

    Strategy: upgrade to Vue 2.7 first (it backports the Composition API and <script setup>), remove deprecated patterns, then use the migration build (@vue/compat), which runs Vue 3 with Vue 2 behaviour where possible and logs warnings for each incompatibility. Third-party component libraries are usually the biggest blocker (for example, moving from Vuetify 2 to 3). Vue 2 reached end of life on 31 December 2023, so staying on it means no security fixes.

    Key Points

    • createApp and app-level APIs.
    • v-model and .sync changes; filters and event bus removed.
    • Go via Vue 2.7 and @vue/compat.
    • Libraries are the main blocker; Vue 2 is EOL.

    Permalink

  19. Most asked How does server-side rendering work with Vue, and what does Nuxt add?

    Summary

    With SSR, the server renders components to HTML (renderToString from vue/server-renderer), sends it so users and crawlers see content immediately, and the client then hydrates it: Vue attaches reactivity and event listeners to the existing DOM. Nuxt is the full-stack framework that handles this for you, with file-based routing, data fetching (useFetch, useAsyncData), server routes, rendering modes and deployment presets.

    Longer Answer

    SSR improves first contentful paint and SEO for content pages, at the cost of server load and complexity: code must be universal (no window in setup), state must be per request (no shared module-level singletons between users), and data fetched on the server must be serialised into the page for hydration. Nuxt also supports static generation, hybrid per-route rules (pre-render some routes, SSR others, client-only for dashboards) and incremental regeneration on supported hosts.

    Key Points

    • Server renders HTML; client hydrates.
    • Better first paint and SEO.
    • Universal code; per-request state.
    • Nuxt: routing, data fetching, rendering modes.

    Permalink

  20. Most asked How do you test Vue components?

    Summary

    Use Vitest with Vue Test Utils (or Testing Library for Vue) to mount components, interact with them as a user would, and assert on rendered output and emitted events; test composables and stores as plain functions; and cover critical journeys end to end with Playwright or Cypress.

    Longer Answer

    Prefer testing behaviour over implementation details: find elements by role or text, trigger events, await updates (await wrapper.find('button').trigger('click') or await nextTick()), and assert what the user sees. Stub child components or network calls (MSW) where needed, use createTestingPinia for stores, and provide router and plugins via global mounting options.

    Key Points

    • Vitest + Vue Test Utils or Testing Library.
    • Test behaviour, not internals.
    • Composables and stores as functions.
    • E2E for critical flows.

    Permalink

  21. Most asked Vue or React: how would you compare them?

    Summary

    Both are mature, component-based libraries with large ecosystems. Vue uses HTML-like templates compiled with optimisation hints, fine-grained reactivity (mutate state, Vue tracks dependencies) and official solutions for routing and state. React uses JSX, immutable state updates with re-rendering from the changed component down, and a larger, more fragmented ecosystem with many third-party choices.

    Longer Answer

    Practical differences: Vue needs less manual memoisation and has a gentler learning curve for people from HTML/CSS backgrounds; React has a bigger job market and ecosystem, React Native for mobile, and server components in frameworks like Next.js. Both are excellent; team experience, hiring and the surrounding stack (Nuxt vs Next.js, Laravel + Inertia supports both) usually decide.

    Key Points

    • Templates + reactivity vs JSX + immutable updates.
    • Official tooling vs larger ecosystem.
    • Less manual memoisation in Vue.
    • Team and ecosystem decide.

    Permalink

  22. Most asked How does Vue's virtual DOM and compiler make updates efficient?

    Summary

    Templates are compiled into render functions that produce virtual DOM. Vue's compiler adds hints: static content is hoisted and skipped on updates, dynamic nodes get patch flags describing what can change (text, class, props), and block trees collect dynamic nodes so the diff only visits those. Combined with fine-grained reactivity, a change re-renders only affected components and patches only dynamic parts.

    Longer Answer

    This 'compiler-informed virtual DOM' is why Vue rarely needs manual memoisation like React.memo. Hand-written render functions and JSX lose some of these optimisations. Vue's team has also been developing Vapor Mode, a compilation strategy that skips the virtual DOM for opted-in components.

    Key Points

    • Templates compile to render functions.
    • Static hoisting, patch flags, block trees.
    • Component-level fine-grained updates.
    • Less manual memoisation.

    Permalink

  23. What does <script setup> give you?
  24. How do you fetch data in Vue components robustly?
  25. What is Vue.js, and what does 'progressive framework' mean?
  26. Why should you not use v-if and v-for on the same element?
  27. How do components emit events, and how do you declare them?
  28. What are provide and inject, and when should you use them?
  29. How do you use a Pinia store correctly in components, and what is storeToRefs for?
  30. How do you lazy-load routes and code-split a Vue application?
  31. What are shallowRef, shallowReactive and markRaw for?
  32. What causes hydration mismatches, and how do you fix them?
  33. How do you make a Vue single-page application SEO-friendly?
  34. How do you use TypeScript with Vue?
  35. How do you handle forms and validation in Vue?
  36. How do you handle errors in a Vue application?
  37. Can you share state without Pinia, and what is the SSR pitfall?
  38. How do sibling components communicate?
  39. A page renders a list of 20,000 rows and is slow. What do you do?
  40. How do memory leaks happen in Vue apps, and how do you prevent them?
  41. How would you structure a large Vue application?
  42. What router history modes exist, and what server configuration does each need?
  43. What are async components and Suspense?
  44. What is Teleport used for?
  45. What does KeepAlive do?
  46. When would you write a custom directive?
  47. What is a Vue plugin, and how do you write one?
  48. What are the pitfalls of deep watchers?
  49. What event modifiers does Vue provide?
  50. What is a writable computed, and when is it useful?
  51. What are fragments and attribute fallthrough in Vue 3?
  52. What role does Vite play in Vue development?
  53. What does defineExpose do?
  54. How can changing a component's key help, and when is it the wrong tool?
  55. What does the `.value` unwrapping rule look like in templates, reactive objects and arrays?
  56. What are Vue's official style-guide rules you consider essential?
  57. How do transitions work in Vue?
  58. How do class and style bindings work?
  59. What does v-memo do, and when would you use v-once?