Vue Interview Questions


Beginners To Experts


The site is under development.

Vue Questions

Vue.js is a progressive JavaScript framework for building user interfaces. It focuses on the view layer, making it easy to integrate with other libraries or existing projects. Vue allows developers to create interactive, reactive, and dynamic web apps efficiently with a component-based architecture.

Components are reusable building blocks in Vue. Each component has its own template, logic, and styling. For example, a button, a navigation bar, or a form could all be individual components. This improves code reusability, readability, and maintainability.

v-if conditionally renders elements in the DOM. If the condition is false, the element is completely removed.
v-show toggles the element’s CSS display property (hides or shows it), but the element always remains in the DOM.
Use v-if for rarely toggled content, and v-show for frequently toggled content.

Vue tracks changes to data properties and automatically updates the DOM when the data changes. This is achieved using getters and setters under the hood. For example, if a variable message changes, Vue ensures the UI reflecting {{ message }} updates instantly without manual DOM manipulation.

Directives are special attributes in Vue that apply reactive behavior to the DOM. Examples:
- v-model: two-way data binding (e.g., form inputs)
- v-bind: dynamically bind attributes
- v-for: render lists
- v-on: handle events (shorthand @)

v-model creates two-way data binding between form inputs and Vue data. When the input changes, the Vue data updates, and when the Vue data changes, the input reflects it.
<input v-model="username" />
<p>Hello, {{ username }}</p>
      

Lifecycle hooks are functions that allow developers to execute code at specific stages of a component’s life. Examples:
- created(): runs after instance creation
- mounted(): runs after the component is added to the DOM
- updated(): runs when data changes cause re-render
- destroyed(): runs when the component is removed

Vue Router is the official library for handling routing in Vue apps. It allows developers to map URLs to components, enabling single-page applications (SPAs) with navigation without reloading the page.
{ path: '/about', component: About }
      

Vuex is a state management library for Vue. It centralizes app-level state, making it easier to manage shared data between multiple components. Use Vuex when your app has complex state logic or when many components need to access the same data.

Computed properties are cached, reactive values derived from Vue data. They are only re-evaluated when dependencies change, unlike methods which run every time they are called.
computed: {
  fullName() {
    return this.firstName + ' ' + this.lastName
  }
}
      

Methods execute a function every time they are called, regardless of whether the underlying data has changed.
Computed properties are cached and only re-run when their dependencies change.
Use computed for derived state, and methods for actions or non-cached logic.

Props are used to pass data from a parent component to a child component. They are read-only.
Data is internal to a component and can be changed within that component.

Vue uses the v-on directive (or @ shorthand) to listen for DOM events. Example:
<button @click="increment">Click Me</button>
      
Here, the increment method will be called when the button is clicked.

v-model provides two-way data binding between form inputs and data.
ref provides a direct reference to a DOM element or component, accessible with this.$refs.

Watchers are used to run logic when a specific data property changes. Example:
watch: {
  count(newVal, oldVal) {
    console.log(`Count changed from ${oldVal} to ${newVal}`);
  }
}
      

Key differences:
- Vue 3 introduces the Composition API (using setup()), while Vue 2 mainly uses the Options API.
- Vue 3 is faster and smaller in size.
- Vue 3 supports Fragments, Teleport, and better TypeScript support.

The Composition API allows developers to organize logic using functions inside setup(). Instead of separating by options (data, methods, computed), you group by feature. Example:
setup() {
  const count = ref(0);
  function increment() { count.value++ }
  return { count, increment }
}
      

The Options API organizes a component using options like data, methods, computed, and watch. Example:
export default {
  data() { return { count: 0 } },
  methods: { increment() { this.count++ } }
}
      

A Single File Component (.vue file) bundles template, script, and style in one file. Example:
<template> <p>{{ message }}</p> </template>
<script>
export default {
  data() { return { message: 'Hello Vue' } }
}
</script>
<style>
p { color: blue; }
</style>
      

Slots are placeholders that allow components to accept and render content passed from the parent. Example:
<slot></slot>
      
It is replaced with the content between component tags.

Named slots let you define multiple insertion points in a component. Example:
<slot name="header"></slot>
<slot name="footer"></slot>
      
You can pass content with <template v-slot:header> in the parent component.

Vue can switch components dynamically using the <component> element with :is. Example:
<component :is="currentView"></component>
      
Here currentView can be "Home", "About", etc.

Lazy loading allows you to load components only when needed, improving performance. Example with Vue Router:
const About = () => import('./About.vue');
      

Vue uses a Virtual DOM to efficiently update the UI. Instead of updating the real DOM directly, Vue updates a lightweight copy (virtual DOM), then calculates the minimal set of changes needed.

Mixins are reusable pieces of logic that can be included in multiple components. Example:
export const myMixin = { 
  created() { console.log('Mixin used!') } 
}
      

Mixins merge functionality but can lead to conflicts and unclear dependencies.
Composition API allows better organization and avoids naming conflicts by grouping logic in setup().

Teleport allows you to render a component’s template in a different part of the DOM outside its parent. Example:
<teleport to="body">
  <div>Modal Content</div>
</teleport>
      

<keep-alive> caches inactive components instead of destroying them. Useful for tabs or forms where you don’t want to lose state. Example:
<keep-alive>
  <component :is="view"></component>
</keep-alive>
      

In Vue 2, filters were used in templates to format text (e.g., currency).
In Vue 3, filters were removed because they added complexity. Now, use computed properties or methods instead.

SSR means rendering Vue components on the server before sending HTML to the client. This improves SEO and initial load performance. Vue provides Nuxt.js as a popular SSR framework.

In Vue 2:
new Vue({
  el: '#app',
  data: { message: 'Hello Vue!' }
});
      
In Vue 3:
import { createApp } from 'vue';
createApp({ data: () => ({ message: 'Hello Vue 3!' }) }).mount('#app');
      

Use v-bind or : shorthand.
<img :src="imageUrl" :alt="description" />
      

Use v-for.
<li v-for="item in items" :key="item.id">{{ item.name }}</li>
      

<div :class="{ active: isActive, error: hasError }"></div>
      

<div :style="{ color: activeColor, fontSize: size + 'px' }"></div>
      

It prevents default browser behavior.
<form @submit.prevent="onSubmit">...</form>
      

It stops event bubbling.
<button @click.stop="doSomething">Click</button>
      

<input type="checkbox" v-model="checked" />
<p>{{ checked }}</p>
      

<input type="checkbox" v-model="fruits" value="Apple">
<input type="checkbox" v-model="fruits" value="Banana">
<p>{{ fruits }}</p>
      

<input v-model="username" placeholder="Enter name" />
<p>Welcome, {{ username }}</p>
      

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

<script>
export default { data: () => ({ count: 0 }) }
</script>
      

props: ['value'],
watch: {
  value(newVal, oldVal) {
    console.log('Changed from', oldVal, 'to', newVal);
  }
}
      

Child:
<button @click="$emit('custom-event', 'hello')">Send</button>
      
Parent:
<Child @custom-event="receiveMessage" />
      

computed: {
  fullName: {
    get() { return this.first + ' ' + this.last },
    set(value) { [this.first, this.last] = value.split(' ') }
  }
}
      

<li v-for="(value, key) in object" :key="key">{{ key }}: {{ value }}</li>
      

<template v-if="type === 'A'">
  <p>A content</p>
</template>
<template v-else-if="type === 'B'">
  <p>B content</p>
</template>
<template v-else>
  <p>Other</p>
</template>
      

<input v-model.lazy="search" />
      

mounted() {
  fetch('https://api.example.com/data')
    .then(res => res.json())
    .then(data => this.items = data)
}
      

import axios from 'axios';
mounted() {
  axios.get('/api/users').then(res => this.users = res.data);
}
      

import { createApp } from 'vue';
import MyComponent from './MyComponent.vue';
const app = createApp(App);
app.component('MyComponent', MyComponent);
app.mount('#app');
      

const AsyncComp = defineAsyncComponent(() => import('./MyComp.vue'));
      

props: {
  title: { type: String, required: true },
  count: { type: Number, default: 0 }
}
      

props: {
  age: {
    type: Number,
    validator: v => v > 0
  }
}
      

<div :class="[isActive ? 'active' : 'inactive']"></div>
      

<div :class="['box', isError ? 'red' : 'green']"></div>
      

<div :style="{ backgroundColor: bgColor, fontSize: size + 'px' }"></div>
      

<template>
  <div>
    <input v-model="newTask" @keyup.enter="addTask" />
    <ul><li v-for="t in tasks" :key="t">{{ t }}</li></ul>
  </div>
</template>

<script>
export default {
  data: () => ({ newTask: '', tasks: [] }),
  methods: { addTask() { this.tasks.push(this.newTask); this.newTask = '' } }
}
</script>
      

<script lang="ts">
import { defineComponent } from 'vue';
export default defineComponent({
  data() { return { count: 0 } }
});
</script>
      

import { mount } from '@vue/test-utils';
import Counter from './Counter.vue';
test('increments on click', () => {
  const wrapper = mount(Counter);
  wrapper.find('button').trigger('click');
  expect(wrapper.text()).toContain('1');
});
      

Build:
npm run build
      
Deploy the generated dist/ folder to a hosting service (Netlify, Vercel, GitHub Pages, etc.).

Vue 3 uses a Proxy-based reactivity system which is faster and can detect property addition/deletion. Vue 2 uses Object.defineProperty, which cannot detect new properties.

Teleport allows rendering a component in a DOM location outside its parent. Useful for modals, tooltips, or overlays that must be outside the component hierarchy.

Suspense lets you handle async components with fallback content while loading. Example:
<Suspense>
  <template #default>
    <AsyncComponent />
  </template>
  <template #fallback>
    Loading...
  </template>
</Suspense>
      

reactive() - creates a reactive object, suitable for multiple properties.
ref() - creates a reactive single value, accessed via .value.
Example:
const state = reactive({ count: 0 });
const count = ref(0);
      

Provide/inject allows sharing data from a parent to deeply nested children without passing props manually.
provide('theme', 'dark');
inject('theme');
      

Vue 3 allows components to catch errors in children using errorCaptured hook. Example:
errorCaptured(err, instance, info) {
  console.log(err, info);
  return false; // stop propagation
}
      

v-once renders content once and never updates. v-memo optimizes re-rendering only for parts that did not change based on dependencies.

<component :is="currentComponent"></component>
currentComponent = 'HomeComponent';
      

v-pre skips compilation for the element and its children. v-cloak hides uncompiled mustache templates until Vue is ready.

Vue 3 supports fragments, so a component can have multiple root elements without wrapping in a div.
<template>
  <h1>Title</h1>
  <p>Paragraph</p>
</template>
      

Fragments allow a Vue 3 component to return multiple root elements without wrapping in an extra div, improving DOM structure.

app.directive('focus', {
  mounted(el) { el.focus(); }
});
<input v-focus />
      

nextTick() waits until DOM updates after data changes, allowing code to run after the updated UI is rendered.
this.message = 'Hello';
this.$nextTick(() => { console.log('DOM updated'); });
      

shallowRef creates a reactive reference but does not make nested objects reactive. ref makes the entire value reactive.

watch([prop1, prop2], ([newVal1, newVal2], [oldVal1, oldVal2]) => {
  console.log(newVal1, newVal2);
});
      

app.mixin({
  created() { console.log('Mixin called in all components'); }
});
      

Async components are loaded only when needed. Example:
const AsyncComp = defineAsyncComponent(() => import('./MyComp.vue'));
      

export default {
  install(app, options) {
    app.config.globalProperties.$myPlugin = () => console.log('Plugin used');
  }
};
      

<transition name="fade">
  <div v-if="show">Hello</div>
</transition>
.fade-enter-active, .fade-leave-active { transition: opacity .5s; }
.fade-enter-from, .fade-leave-to { opacity: 0; }
      

<input ref="myInput" />
this.$refs.myInput.focus();
      

import { reactive } from 'vue';
const state = reactive({ count: 0, message: 'Hello' });
      

provide/inject is for passing data from parent to child deeply without props. Vuex is a centralized state management system for complex apps.

import { shallowReactive } from 'vue';
const state = shallowReactive({ nested: { count: 0 } });
      

Watchers are used for performing asynchronous or expensive operations in response to data changes, e.g., API calls when input changes.

import { ref } from 'vue';
const count = ref(0);
const message = ref('Hello');
      

<li v-for="item in items" :key="item.id">{{ item.name }}</li>
      

<slot>Default content</slot>
      

Emits allow child components to send events to parent components. In setup(), define emits:
defineEmits(['update']);
emit('update', value);
      

const routes = [
  { path: '/about', component: () => import('./About.vue') }
];
      

<MyComponent :title="dynamicTitle" />
      

import { computed, ref } from 'vue';
const first = ref('John');
const last = ref('Doe');
const fullName = computed(() => first.value + ' ' + last.value);
      

defineAsyncComponent loads a component only when needed, reducing initial bundle size:
const AsyncComp = defineAsyncComponent(() => import('./Comp.vue'));
      

<div :style="{ color: isError ? 'red' : 'green' }">Message</div>
      

<component :is="currentView"></component>
      

import { ref, onMounted } from 'vue';
const myInput = ref(null);
onMounted(() => { myInput.value.focus(); });
<input ref="myInput" />
      

import { reactive, ref } from 'vue';
const count = ref(0);
const message = ref('Hello');
const state = reactive({ count, message });
      

Use errorCaptured hook in parent component:
errorCaptured(err, vm, info) {
  console.error(err);
  return false; // stop propagation
}
      

defineEmits(['update', 'delete']);
emit('update', data);
emit('delete', id);
      

<Suspense>
  <template #default>
    <AsyncComp />
  </template>
  <template #fallback>
    Loading...
  </template>
</Suspense>
      

mounted() {
  fetch('https://api.example.com/data')
    .then(res => res.json())
    .then(data => this.items = data);
}
      

<template>
  <div v-if="show">
    <div class="modal">
      <slot>Modal Content</slot>
    </div>
  </div>
</template>

<script>
export default {
  props: ['show']
}
</script>
      

app.directive('focus', {
  mounted(el) { el.focus(); }
});
<input v-focus />
      

import mitt from 'mitt';
export const bus = mitt();

bus.on('eventName', payload => console.log(payload));
bus.emit('eventName', { message: 'Hello' });
      

<table>
  <tr v-for="row in tableData" :key="row.id">
    <td v-for="(value, key) in row" :key="key">{{ value }}</td>
  </tr>
</table>
      

<button @click="isActive = !isActive">Toggle</button>
<p>Status: {{ isActive ? 'On' : 'Off' }}</p>

data() {
  return { isActive: false }
}
      

<div>
  <button v-for="tab in tabs" :key="tab" @click="currentTab = tab">{{ tab }}</button>
  <div v-for="tab in tabs" v-show="currentTab === tab">Content for {{ tab }}</div>
</div>

data() {
  return { tabs: ['Tab1','Tab2'], currentTab: 'Tab1' }
}
      

<div>
  <button @click="showMenu = !showMenu">Menu</button>
  <ul v-if="showMenu">
    <li v-for="item in items" :key="item">{{ item }}</li>
  </ul>
</div>

data() { return { showMenu: false, items: ['One','Two','Three'] } }
      

<form @submit.prevent="submitForm">
  <input v-model="email" />
  <span v-if="!isValid">Invalid email</span>
  <button type="submit">Submit</button>
</form>

data() { return { email: '' } },
computed: {
  isValid() { return this.email.includes('@') }
},
methods: { submitForm() { alert('Form submitted') } }
      

<div @mouseenter="show=true" @mouseleave="show=false">
  Hover me
  <span v-if="show">Tooltip text</span>
</div>

data() { return { show: false } }
      

<template>
  <input :value="modelValue" @input="$emit('update:modelValue',$event.target.value)" />
</template>

props: ['modelValue']
      

import { reactive } from 'vue';
const state = reactive({ count: 0, message: 'Hello' });
state.count++;
state.message = 'Hi';
      

import { ref, watchEffect } from 'vue';
const count = ref(0);
watchEffect(() => { console.log(`Count is: ${count.value}`); });
count.value++;
      

async mounted() {
  const res = await fetch('https://api.example.com');
  const data = await res.json();
  this.items = data;
}
      

<Child :modelValue="parentData" @update:modelValue="parentData = $event" />
data() { return { parentData: '' } }
      

<Suspense>
  <template #default><component :is="currentView" /></template>
  <template #fallback>Loading...</template>
</Suspense>
      

<li v-for="(item, index) in items" :key="index">{{ index }} - {{ item }}</li>
      

<button @click="handleEvent" @mouseover="handleEvent">Click or Hover</button>
methods: { handleEvent() { console.log('Event triggered') } }
      

import { reactive } from 'vue';
const map = reactive(new Map());
map.set('key', 'value');
console.log(map.get('key'));
      

<div v-if="loading">Loading...</div>
<div v-else>Content Loaded</div>
data() { return { loading: true } },
mounted() { setTimeout(() => this.loading = false, 2000) }
      

<div v-show="isVisible">Visible Content</div>
data() { return { isVisible: true } }
      

data() { return { time: 10 } },
mounted() {
  const timer = setInterval(() => {
    if(this.time > 0) this.time--;
    else clearInterval(timer);
  }, 1000);
}
<div>{{ time }} seconds left</div>
      

<th @click="sortBy('name')">Name</th>
<tr v-for="item in sortedItems" :key="item.id">
  <td>{{ item.name }}</td>
</tr>

computed: {
  sortedItems() {
    return this.items.sort((a,b) => a.name.localeCompare(b.name));
  }
},
methods: { sortBy(key) { /* change sort key logic */ } }
      

<div v-for="(item, index) in items" :key="index">
  <h3 @click="active = index">{{ item.title }}</h3>
  <div v-show="active === index">{{ item.content }}</div>
</div>
data() { return { active: null, items: [...] } }
      

<div v-if="step === 1">Step 1 Content</div>
<div v-else-if="step === 2">Step 2 Content</div>
<button @click="step++">Next</button>
data() { return { step: 1 } }
      

<input v-model="search" placeholder="Search">
<li v-for="item in filteredItems" :key="item">{{ item }}</li>

computed: {
  filteredItems() {
    return this.items.filter(i => i.includes(this.search));
  }
}
      

<li v-for="item in pagedItems" :key="item">{{ item }}</li>
<button @click="page--">Prev</button>
<button @click="page++">Next</button>

computed: {
  pagedItems() {
    const start = (this.page-1)*this.pageSize;
    return this.items.slice(start, start + this.pageSize);
  }
},
data() { return { page: 1, pageSize: 5 } }
      

<div :style="{ backgroundColor: color }">Colored Div</div>
data() { return { color: 'red' } }
      

<div v-for="n in notifications" :key="n.id">{{ n.message }}</div>
methods: { addNotification(msg) { this.notifications.push({ id: Date.now(), message: msg }) } },
data() { return { notifications: [] } }
      

<div :class="{ sticky: isSticky }">Header</div>
data() { return { isSticky: false } },
mounted() {
  window.addEventListener('scroll', () => { this.isSticky = window.scrollY > 100 })
}
      

<div :style="{ width: progress + '%', backgroundColor: 'green' }"></div>
data() { return { progress: 0 } },
mounted() { setInterval(() => { if(progress<100) this.progress += 1 }, 100) }
      

<div :class="{ dark: isDark }">Content</div>
<button @click="isDark = !isDark">Toggle Dark</button>
data() { return { isDark: false } }
      

<Modal v-if="showModal" @close="showModal = false">Content</Modal>
data() { return { showModal: false } },
methods: { openModal() { this.showModal = true } }
      

<div draggable="true" @dragstart="dragStart(item)">Drag Me</div>
methods: {
  dragStart(item) { this.dragged = item }
},
data() { return { dragged: null } }
      

window.addEventListener('scroll', () => {
  if(window.innerHeight + window.scrollY >= document.body.offsetHeight) {
    this.loadMore();
  }
});
methods: { loadMore() { this.items.push(...newItems) } }
      

<input v-model.lazy="search">
data() { return { search: '' } }
      

<button v-for="(tab,index) in tabs" @click="active=index">{{ tab }}</button>
<div v-for="(tab,index) in tabs" v-show="active===index">Content {{ tab }}</div>
data() { return { tabs:['A','B'], active:0 } }
      

<div v-cloak>{{ message }}</div>
<style> [v-cloak] { display:none; } </style>
      

<div @mouseenter="show=true" @mouseleave="show=false">Hover Me
  <span v-if="show">Tooltip</span>
</div>
data() { return { show:false } }
      

<transition name="fade">
  <div v-if="show">Content</div>
</transition>
data() { return { show: true } }
<style>
.fade-enter-active, .fade-leave-active { transition: opacity 0.5s; }
.fade-enter, .fade-leave-to { opacity:0; }
</style>
      

<div v-for="(field,index) in fields" :key="index">
  <input v-model="field.value" />
</div>
data() { return { fields:[{value:''},{value:''}] } }
      

import { ref, onMounted } from 'vue';
const time = ref(10);
onMounted(() => {
  const timer = setInterval(() => {
    if(time.value>0) time.value--;
    else clearInterval(timer);
  },1000);
});
      

<img v-lazy="imageUrl" alt="Lazy Image">
import VueLazyload from 'vue-lazyload';
app.use(VueLazyload);
      

app.directive('tooltip', {
  mounted(el, binding) {
    el.setAttribute('title', binding.value);
  }
});
<button v-tooltip="'Tooltip text'">Hover Me</button>
      

router.beforeEach((to, from, next) => {
  if(to.meta.requiresAuth && !isLoggedIn()) next('/login');
  else next();
});
      

import draggable from 'vuedraggable';
<draggable v-model="list">
  <div v-for="item in list" :key="item">{{ item }}</div>
</draggable>
data() { return { list: ['A','B','C'] } }
      

<div v-for="(img,index) in images" v-show="current===index"><img :src="img"></div>
<button @click="prev">Prev</button>
<button @click="next">Next</button>
data() { return { images:['a.jpg','b.jpg'], current:0 } },
methods: { prev(){ this.current = (this.current-1+this.images.length)%this.images.length }, next(){ this.current=(this.current+1)%this.images.length } }
      

<input v-model="email">
<span v-if="!isValidEmail">Invalid Email</span>
computed: { isValidEmail(){ return /^[^@]+@[^@]+\.[^@]+$/.test(this.email) } },
data() { return { email:'' } }
      

data() { return { time: 10000 } },
mounted() {
  const timer = setInterval(() => {
    if(this.time>0) this.time-=100;
    else clearInterval(timer);
  },100);
}
<div>{{ (time/1000).toFixed(1) }}s</div>
      

<svg viewBox="0 0 36 36">
  <circle cx="18" cy="18" r="16" stroke-width="4" :stroke-dasharray="progress+',100'" />
</svg>
data() { return { progress: 50 } }
      

import jsPDF from 'jspdf';
methods: { downloadPDF() {
  const doc = new jsPDF();
  doc.text("Hello Vue PDF", 10, 10);
  doc.save("file.pdf");
} }
<button @click="downloadPDF">Download PDF</button>
      

<header :class="{ sticky: isSticky }">Header</header>
data() { return { isSticky:false } },
mounted() {
  window.addEventListener('scroll', () => this.isSticky = window.scrollY>100)
}
      

<label>
  <input type="checkbox" v-model="isOn">
  <span>Toggle</span>
</label>
data() { return { isOn:false } }
      

<span v-for="n in 5" :key="n" @click="rating=n">{{ n <= rating ? '★':'☆' }}</span>
data() { return { rating:0 } }
      

<input type="file" @change="onFileChange">
methods: { onFileChange(e){ this.file = e.target.files[0] } },
data() { return { file:null } }
      

data() { return { time:3600 } },
computed: {
  hours() { return Math.floor(this.time/3600) },
  minutes() { return Math.floor((this.time%3600)/60) },
  seconds() { return this.time%60 }
},
mounted() {
  setInterval(() => { if(this.time>0)this.time-- },1000)
}
      

<div v-if="showToast">Notification</div>
methods: { show(){ this.showToast=true; setTimeout(()=>this.showToast=false,3000) } },
data() { return { showToast:false } }
      

<input v-model="query">
<li v-for="item in filteredItems">{{ item }}</li>
computed: {
  filteredItems() { return this.items.filter(i => i.includes(this.query)) }
},
data() { return { items:['apple','banana'], query:'' } }
      

<input :type="show?'text':'password'" v-model="password">
<button @click="show=!show">Toggle</button>
data() { return { password:'', show:false } }
      

<aside :class="{ open:isOpen }">Sidebar</aside>
<button @click="isOpen=!isOpen">Toggle</button>
data() { return { isOpen:false } }
      

<th @click="sort('name')">Name</th>
<tr v-for="item in sorted"><td>{{ item.name }}</td></tr>
data() { return { items:[{name:'A'},{name:'B'}], sortKey:'', asc:true } },
computed: {
  sorted() { return this.items.sort((a,b)=>this.asc?(a[this.sortKey]>b[this.sortKey]?1:-1):(a[this.sortKey]
      
    

<div v-for="item in items">{{ item }}</div>
window.addEventListener('scroll', () => {
  if(window.innerHeight + window.scrollY > document.body.offsetHeight-10) loadMore()
});
methods: { loadMore(){ this.items.push(...moreItems) } },
data() { return { items:[] } }
      

<div v-if="showModal">Modal Content</div>
<button @click="showModal=true">Open</button>
<button @click="showModal=false">Close</button>
data() { return { showModal:false } }
      

<div @drop.prevent="onDrop" @dragover.prevent>Drop files here</div>
methods: { onDrop(e){ this.files = e.dataTransfer.files } },
data() { return { files:[] } }
      

<li v-for="(crumb,index) in breadcrumbs">{{ crumb }}</li>
data() { return { breadcrumbs:['Home','About','Profile'] } }
      

<div v-if="step===1">Step 1</div>
<div v-else-if="step===2">Step 2</div>
<button @click="nextStep">Next</button>
data() { return { step:1 } },
methods: { nextStep(){ if(this.step<2)this.step++ } }
      

<div v-for="img in images"><img :src="img"></div>
data() { return { images:['1.jpg','2.jpg','3.jpg'] } }
      

<button @click="open=!open">Menu</button>
<ul v-if="open"><li>Item 1</li><li>Item 2</li></ul>
data() { return { open:false } }
      

<button v-for="(t,index) in tabs" @click="active=index">{{ t }}</button>
<div v-for="(t,index) in tabs" v-show="active===index">{{ t }} Content</div>
data() { return { tabs:['Tab1','Tab2'], active:0 } }
      

<div :style="{ width: progress+'%' }">{{ progress }}%</div>
data() { return { progress:0 } },
methods: { increase(){ if(this.progress<100)this.progress+=10 } }
      

<div @click="open=!open">Header</div>
<div v-if="open">Content</div>
data() { return { open:false } }
      

<input type="date" v-model="selectedDate">
data() { return { selectedDate:'' } }
      

<input type="time" v-model="selectedTime">
data() { return { selectedTime:'' } }
      

<button @click="count--">-</button>
<span>{{ count }}</span>
<button @click="count++">+</button>
data() { return { count:0 } }
      

<div class="table-responsive">
  <table>
    <tr v-for="item in items"><td>{{ item.name }}</td></tr>
  </table>
</div>
data() { return { items:[{name:'A'},{name:'B'}] } }
      

<select v-model="selected" multiple>
  <option v-for="opt in options" :value="opt">{{ opt }}</option>
</select>
data() { return { selected:[], options:['A','B','C'] } }
      

<span @mouseover="show=true" @mouseleave="show=false">Hover me</span>
<div v-if="show">Tooltip Content</div>
data() { return { show:false } }
      

<div v-if="showModal">{{ content }}</div>
<button @click="open('Hello')">Open</button>
methods: { open(msg){ this.content=msg; this.showModal=true } },
data() { return { showModal:false, content:'' } }
      

<div :class="{ flipped:isFlipped }" @click="isFlipped=!isFlipped">Front/Back</div>
data() { return { isFlipped:false } }
      

data() { return { time:10 } },
mounted() {
  let timer=setInterval(()=>{
    if(this.time>0)this.time--;
    else clearInterval(timer)
  },1000)
}
      

<input v-model="newTag" @keyup.enter="addTag">
<span v-for="tag in tags">{{ tag }}</span>
data() { return { newTag:'', tags:[] } },
methods: { addTag(){ this.tags.push(this.newTag); this.newTag='' } }
      

<div v-for="img in images"><img :src="img"></div>
methods: { next(){ this.index = (this.index+1) % this.images.length } },
data() { return { images:['1.jpg','2.jpg'], index:0 } }
      

<span v-for="n in 5" @click="rating=n">★</span>
data() { return { rating:0 } }
      

<input :type="show?'text':'password'" v-model="password">
<button @click="show=!show">Toggle</button>
data() { return { password:'', show:false } }
      

<div :class="{ collapsed:isCollapsed }">Sidebar</div>
<button @click="isCollapsed=!isCollapsed">Toggle</button>
data() { return { isCollapsed:false } }
      

<span @mouseover="show=true" @mouseleave="show=false">Hover</span>
<div v-if="show">{{ tooltipText }}</div>
data() { return { show:false, tooltipText:'Dynamic Tooltip' } }
      

<td v-for="cell in row"><input v-model="cell.value"></td>
data() { return { table:[[{value:'A'},{value:'B'}]] } }
      

<input v-model="query">
<li v-for="item in items.filter(i => i.includes(query))">{{ item }}</li>
data() { return { query:'', items:['Apple','Banana'] } }
      

Vue.directive('focus', { inserted(el){ el.focus() } })
<input v-focus>
      

<component :is="currentComponent"></component>
data() { return { currentComponent:'CompA' } }
      

<textarea v-model="md"></textarea>
<div v-html="compiledMarkdown"></div>
computed: { compiledMarkdown(){ return marked(this.md) } },
data() { return { md:'' } }
      

<div v-if="step===1">Step 1</div>
<div v-else-if="step===2">Step 2</div>
<button @click="step++">Next</button>
data() { return { step:1 } }
      

<button v-tooltip="'Tooltip Text'">Hover</button>
import VTooltip from 'v-tooltip';
app.use(VTooltip)
      

<draggable v-model="items"><div v-for="i in items">{{ i }}</div></draggable>
data() { return { items:['A','B','C'] } }
      

<button @click="showToast">Show</button>
<div v-if="toast">Notification</div>
methods:{ showToast(){ this.toast=true; setTimeout(()=>this.toast=false,2000) } },
data(){ return { toast:false } }
      

<button @click="scrollTop">Top</button>
methods:{ scrollTop(){ window.scrollTo(0,0) } }
      

<div>{{ time }}</div>
mounted(){
  let t=setInterval(()=>{ if(this.time>0)this.time--; else clearInterval(t) },1000)
},
data(){ return { time:10 } }
      

<header :class="{ sticky:isSticky }">Header</header>
mounted(){ window.addEventListener('scroll',()=>this.isSticky=window.scrollY>50) },
data(){ return { isSticky:false } }
      

<div @drop.prevent="upload" @dragover.prevent>Drop file</div>
methods:{ upload(e){ this.file=e.dataTransfer.files[0] } },
data(){ return { file:null } }
      

<button @click="dark=!dark">Toggle</button>
<div :class="{ dark:dark }">Content</div>
data(){ return { dark:false } }
      

<button @click="theme=theme==='light'?'dark':'light'">Switch</button>
<div :class="theme">Content</div>
data(){ return { theme:'light' } }