Skip to main content
New: Deck Doctor. Upload your deck, get CPO-level feedback. 7-day free trial.
Free Resource

Vue.js Cheat Sheet

Complete Vue 3 quick reference with Composition API, directives, lifecycle hooks, reactivity, and more. 40 entries across 7 categories with copy-paste code snippets.

1

Search or browse by topic

2

Read the syntax and examples

3

Copy code into your Vue project

Text Interpolation {{ }}

Vue 3

Display reactive data in the template using double curly braces.

<template>
  <p>{{ message }}</p>
  <p>{{ count + 1 }}</p>
  <p>{{ ok ? 'YES' : 'NO' }}</p>
</template>

<script setup>
import { ref } from 'vue'

const message = ref('Hello Vue!')
const count = ref(0)
const ok = ref(true)
</script>

v-bind / :

Vue 3

Dynamically bind attributes or props to an expression.

<!-- Full syntax -->
<img v-bind:src="imageUrl" />

<!-- Shorthand -->
<img :src="imageUrl" />
<a :href="url" :class="{ active: isActive }">Link</a>

<!-- Bind multiple attrs with an object -->
<div v-bind="{ id: someId, class: someClass }"></div>

v-on / @

Vue 3

Attach event listeners to elements. Supports modifiers.

<!-- Full syntax -->
<button v-on:click="handleClick">Click</button>

<!-- Shorthand -->
<button @click="count++">Add 1</button>

<!-- Modifiers -->
<form @submit.prevent="onSubmit">...</form>
<input @keyup.enter="submit" />
<button @click.once="doOnce">Once</button>

v-if / v-else-if / v-else

Vue 3

Conditionally render elements. Elements are destroyed and re-created.

<div v-if="type === 'A'">Type A</div>
<div v-else-if="type === 'B'">Type B</div>
<div v-else>Default</div>

<!-- Use <template> for multiple elements -->
<template v-if="show">
  <h1>Title</h1>
  <p>Content</p>
</template>

v-for with :key

Vue 3

Render a list of items. Always provide a unique :key.

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

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

<!-- Object -->
<div v-for="(value, key) in myObject" :key="key">
  {{ key }}: {{ value }}
</div>

v-model

Vue 3

Two-way data binding on form inputs, textareas, and components.

<input v-model="text" />
<textarea v-model="message"></textarea>

<!-- Modifiers -->
<input v-model.trim="name" />
<input v-model.number="age" type="number" />
<input v-model.lazy="query" />

<!-- On components -->
<MyInput v-model="searchText" />
<MyInput v-model:title="pageTitle" />

v-show

Vue 3

Toggle visibility via CSS display. Element stays in DOM.

<!-- Toggles display: none -->
<p v-show="isVisible">Now you see me</p>

<!-- v-show vs v-if:
     v-show: always rendered, toggles CSS
     v-if: actually destroys/creates elements
     Use v-show for frequent toggling
     Use v-if for rare changes -->

v-slot / #

Vue 3

Declare slot content when using child components.

<!-- Default slot -->
<MyComponent>
  <p>Slot content here</p>
</MyComponent>

<!-- Named slots -->
<MyLayout>
  <template #header>
    <h1>Page Title</h1>
  </template>
  <template #default>
    <p>Main content</p>
  </template>
  <template #footer>
    <p>Footer</p>
  </template>
</MyLayout>

ref()

Vue 3

Create a reactive reference for primitive or object values. Access via .value in script.

import { ref } from 'vue'

const count = ref(0)
const name = ref('Vue')

// Access in script: use .value
count.value++
console.log(name.value)

// In template: auto-unwrapped (no .value needed)
// <p>{{ count }}</p>

reactive()

Vue 3

Create a reactive object. No .value needed but cannot reassign the whole object.

import { reactive } from 'vue'

const state = reactive({
  count: 0,
  user: { name: 'Alice' },
  items: []
})

// Direct property access
state.count++
state.user.name = 'Bob'
state.items.push('new item')

computed()

Vue 3

Create a cached computed value that updates when dependencies change.

import { ref, computed } from 'vue'

const items = ref([1, 2, 3, 4, 5])

// Read-only computed
const evenItems = computed(() =>
  items.value.filter(n => n % 2 === 0)
)

// Writable computed
const fullName = computed({
  get: () => firstName.value + ' ' + lastName.value,
  set: (val) => {
    [firstName.value, lastName.value] = val.split(' ')
  }
})

watch() / watchEffect()

Vue 3

Run side effects when reactive data changes.

import { ref, watch, watchEffect } from 'vue'

const count = ref(0)
const name = ref('Vue')

// Watch specific source
watch(count, (newVal, oldVal) => {
  console.log(newVal, oldVal)
})

// Watch multiple sources
watch([count, name], ([newCount, newName]) => {
  console.log(newCount, newName)
})

// Auto-track dependencies
watchEffect(() => {
  console.log('Count is:', count.value)
})

onMounted() / onUnmounted() / onUpdated()

Vue 3

Lifecycle hooks for setup, cleanup, and DOM updates.

import {
  onMounted,
  onUnmounted,
  onUpdated,
  onBeforeMount,
  onBeforeUpdate,
  onBeforeUnmount
} from 'vue'

onMounted(() => {
  console.log('Component mounted')
  window.addEventListener('resize', onResize)
})

onUnmounted(() => {
  window.removeEventListener('resize', onResize)
})

onUpdated(() => {
  console.log('DOM updated')
})

defineProps() / defineEmits()

Vue 3

Declare component props and emitted events in script setup.

<script setup>
// Props with types
const props = defineProps({
  title: String,
  count: { type: Number, default: 0 },
  items: { type: Array, required: true }
})

// TypeScript props
const props = defineProps<{
  title: string
  count?: number
}>()

// Emits
const emit = defineEmits(['update', 'delete'])
emit('update', newValue)

// TypeScript emits
const emit = defineEmits<{
  (e: 'update', value: string): void
  (e: 'delete', id: number): void
}>()
</script>

provide() / inject()

Vue 3

Pass data down the component tree without prop drilling.

// Parent component
import { provide, ref } from 'vue'

const theme = ref('dark')
provide('theme', theme)
provide('appName', 'My App')

// Child / grandchild component
import { inject } from 'vue'

const theme = inject('theme')
const appName = inject('appName', 'Default App')

toRef() / toRefs()

Vue 3

Create refs from reactive object properties while keeping reactivity.

import { reactive, toRef, toRefs } from 'vue'

const state = reactive({
  name: 'Vue',
  count: 0
})

// Single property ref
const nameRef = toRef(state, 'name')

// Destructure while keeping reactivity
const { name, count } = toRefs(state)

// Useful in composables
function useFeature(props) {
  const title = toRef(props, 'title')
  // title.value stays in sync with props.title
}

SFC Structure

Vue 3

Single File Component with script setup, template, and scoped styles.

<script setup>
import { ref } from 'vue'
import ChildComponent from './ChildComponent.vue'

const count = ref(0)
</script>

<template>
  <div class="wrapper">
    <h1>{{ count }}</h1>
    <button @click="count++">Add</button>
    <ChildComponent :value="count" />
  </div>
</template>

<style scoped>
.wrapper {
  padding: 1rem;
}
</style>

Props with Types

Vue 3

Define typed props with defaults and validation.

<script setup>
// Runtime declaration
const props = defineProps({
  status: {
    type: String,
    required: true,
    validator: (v) => ['active', 'inactive'].includes(v)
  },
  items: {
    type: Array,
    default: () => []
  }
})

// TypeScript with defaults
interface Props {
  title: string
  count?: number
  items?: string[]
}
const props = withDefaults(defineProps<Props>(), {
  count: 0,
  items: () => []
})
</script>

Events / Emits

Vue 3

Emit custom events from child to parent components.

<!-- Child component -->
<script setup>
const emit = defineEmits(['update', 'close'])

function save() {
  emit('update', { id: 1, name: 'Updated' })
}
</script>

<template>
  <button @click="save">Save</button>
  <button @click="emit('close')">Close</button>
</template>

<!-- Parent component -->
<template>
  <ChildComponent
    @update="handleUpdate"
    @close="showModal = false"
  />
</template>

Slots (default / named / scoped)

Vue 3

Content distribution with default, named, and scoped slots.

<!-- MyCard.vue -->
<template>
  <div class="card">
    <header><slot name="header" /></header>
    <main><slot /></main>
    <footer>
      <slot name="footer" :year="2024" />
    </footer>
  </div>
</template>

<!-- Usage -->
<MyCard>
  <template #header>
    <h2>Card Title</h2>
  </template>

  <p>Default slot content</p>

  <template #footer="{ year }">
    <small>Copyright {{ year }}</small>
  </template>
</MyCard>

Dynamic Components

Vue 3

Switch between components dynamically using :is.

<script setup>
import { ref, shallowRef } from 'vue'
import TabHome from './TabHome.vue'
import TabProfile from './TabProfile.vue'
import TabSettings from './TabSettings.vue'

const tabs = { home: TabHome, profile: TabProfile, settings: TabSettings }
const current = shallowRef('home')
</script>

<template>
  <button v-for="(_, name) in tabs" :key="name"
    @click="current = name">
    {{ name }}
  </button>
  <component :is="tabs[current]" />
</template>

Async Components

Vue 3

Lazy-load components for code splitting.

import { defineAsyncComponent } from 'vue'

const AsyncModal = defineAsyncComponent(() =>
  import('./components/Modal.vue')
)

// With loading and error states
const AsyncDashboard = defineAsyncComponent({
  loader: () => import('./Dashboard.vue'),
  loadingComponent: LoadingSpinner,
  errorComponent: ErrorDisplay,
  delay: 200,
  timeout: 3000
})

ref vs reactive

Vue 3

When to use ref() versus reactive() for state management.

import { ref, reactive } from 'vue'

// ref: works with any value type
const count = ref(0)          // primitive
const user = ref({ name: 'A' }) // object (deeply reactive)
count.value++
user.value.name = 'B'

// reactive: objects only, no .value
const state = reactive({ count: 0, name: 'A' })
state.count++

// Rules of thumb:
// - ref for primitives (string, number, boolean)
// - ref or reactive for objects (ref is more flexible)
// - reactive cannot be reassigned: state = newObj (breaks)
// - ref can: user.value = newObj (works fine)

shallowRef / shallowReactive

Vue 3

Only track top-level reactivity for performance optimization.

import { shallowRef, shallowReactive, triggerRef } from 'vue'

// Only .value assignment is reactive
const state = shallowRef({ nested: { count: 0 } })
state.value.nested.count++ // NOT reactive
state.value = { nested: { count: 1 } } // reactive

// Force trigger after shallow mutation
triggerRef(state)

// Only top-level props are reactive
const obj = shallowReactive({
  count: 0,       // reactive
  nested: { a: 1 } // NOT reactive
})

readonly()

Vue 3

Create a read-only proxy of a reactive object.

import { reactive, readonly } from 'vue'

const original = reactive({ count: 0 })
const copy = readonly(original)

// copy.count++ // Warning! Cannot mutate
original.count++ // This works, copy reflects it

// Useful for providing data to child components
// that should not be mutated
provide('config', readonly(appConfig))

nextTick()

Vue 3

Wait for the DOM to update after a state change.

import { ref, nextTick } from 'vue'

const message = ref('Hello')

async function updateAndRead() {
  message.value = 'Updated'

  // DOM hasn't updated yet
  console.log(document.getElementById('msg').textContent)
  // Still shows 'Hello'

  await nextTick()

  // DOM is now updated
  console.log(document.getElementById('msg').textContent)
  // Shows 'Updated'
}

Reactivity Caveats

Vue 3

Common pitfalls with Vue reactivity system.

import { reactive, ref, toRefs } from 'vue'

const state = reactive({ count: 0 })

// DON'T: destructure reactive (loses reactivity)
let { count } = state // count is now a plain number

// DO: use toRefs to keep reactivity
const { count } = toRefs(state) // count is a ref

// DON'T: reassign reactive object
let state = reactive({ a: 1 })
state = reactive({ a: 2 }) // original watchers lost

// DO: mutate properties instead
state.a = 2

// DON'T: replace ref array content like this
const list = ref([1, 2, 3])
// list.value = list.value.filter(...)  // OK
// list.value.length = 0  // OK
// Use .push(), .splice() for mutations

router-link / router-view

Vue 3

Declarative navigation and route outlet.

<template>
  <nav>
    <router-link to="/">Home</router-link>
    <router-link to="/about">About</router-link>
    <router-link :to="{ name: 'user', params: { id: 1 }}">
      User 1
    </router-link>
  </nav>

  <!-- Route component renders here -->
  <router-view />

  <!-- Named views -->
  <router-view name="sidebar" />
</template>

useRouter() / useRoute()

Vue 3

Access router instance and current route in Composition API.

<script setup>
import { useRouter, useRoute } from 'vue-router'

const router = useRouter()
const route = useRoute()

// Navigate programmatically
router.push('/dashboard')
router.push({ name: 'user', params: { id: 42 }})
router.replace('/login')
router.go(-1) // back

// Access current route info
console.log(route.path)      // '/users/42'
console.log(route.params.id) // '42'
console.log(route.query.q)   // from ?q=search
console.log(route.hash)      // from #section
</script>

Route Params / Query

Vue 3

Define and access dynamic route segments and query strings.

// router/index.js
const routes = [
  // Dynamic param
  { path: '/user/:id', name: 'user', component: User },

  // Optional param
  { path: '/posts/:year?', component: Posts },

  // Multiple params
  { path: '/org/:orgId/team/:teamId', component: Team },

  // Catch-all
  { path: '/:pathMatch(.*)*', component: NotFound }
]

// In component
const route = useRoute()
route.params.id      // from /user/42
route.query.page     // from ?page=2

Navigation Guards

Vue 3

Control navigation with before/after hooks.

// Global guard (router/index.js)
router.beforeEach((to, from) => {
  if (to.meta.requiresAuth && !isAuthenticated()) {
    return { name: 'login' }
  }
})

// Per-route guard
{
  path: '/admin',
  component: Admin,
  beforeEnter: (to, from) => {
    if (!isAdmin()) return false
  }
}

// In-component guard
import { onBeforeRouteLeave } from 'vue-router'

onBeforeRouteLeave((to, from) => {
  if (hasUnsavedChanges) {
    return confirm('Discard changes?')
  }
})

Lazy Loading

Vue 3

Code-split routes for faster initial load.

const routes = [
  {
    path: '/',
    component: () => import('./views/Home.vue')
  },
  {
    path: '/dashboard',
    component: () => import('./views/Dashboard.vue')
  },

  // Group chunks with webpackChunkName
  {
    path: '/settings',
    component: () => import(
      /* webpackChunkName: "settings" */
      './views/Settings.vue'
    )
  }
]

defineStore

Vue 3

Create a Pinia store with state, getters, and actions.

// stores/counter.js
import { defineStore } from 'pinia'

// Option syntax
export const useCounterStore = defineStore('counter', {
  state: () => ({ count: 0, name: 'Counter' }),
  getters: {
    doubleCount: (state) => state.count * 2
  },
  actions: {
    increment() {
      this.count++
    }
  }
})

// Setup syntax (Composition API style)
export const useCounterStore = defineStore('counter', () => {
  const count = ref(0)
  const doubleCount = computed(() => count.value * 2)
  function increment() { count.value++ }
  return { count, doubleCount, increment }
})

State / Getters / Actions

Vue 3

Use store state, computed getters, and methods in components.

<script setup>
import { useCounterStore } from '@/stores/counter'

const store = useCounterStore()

// Read state and getters
store.count
store.doubleCount

// Call actions
store.increment()

// Direct mutation
store.count++

// Patch multiple values
store.$patch({
  count: 10,
  name: 'Updated'
})

// Reset to initial state
store.$reset()

// Subscribe to changes
store.$subscribe((mutation, state) => {
  localStorage.setItem('counter', JSON.stringify(state))
})
</script>

storeToRefs

Vue 3

Destructure store properties while keeping reactivity.

<script setup>
import { storeToRefs } from 'pinia'
import { useUserStore } from '@/stores/user'

const store = useUserStore()

// DON'T: loses reactivity
// const { name, email } = store

// DO: use storeToRefs for state/getters
const { name, email, fullName } = storeToRefs(store)

// Actions can be destructured directly
const { updateProfile, logout } = store
</script>

<template>
  <p>{{ name }}</p>
  <button @click="logout">Logout</button>
</template>

Store Composition

Vue 3

Use one store inside another for shared state.

// stores/auth.js
export const useAuthStore = defineStore('auth', () => {
  const user = ref(null)
  const isLoggedIn = computed(() => !!user.value)
  return { user, isLoggedIn }
})

// stores/cart.js
export const useCartStore = defineStore('cart', () => {
  const auth = useAuthStore()
  const items = ref([])

  const canCheckout = computed(() =>
    auth.isLoggedIn && items.value.length > 0
  )

  async function checkout() {
    if (!auth.isLoggedIn) throw new Error('Login required')
    // process checkout...
  }

  return { items, canCheckout, checkout }
})

Teleport

Vue 3

Render content in a different part of the DOM tree.

<!-- Renders inside <body>, not in parent component -->
<Teleport to="body">
  <div class="modal-overlay">
    <div class="modal">
      <h2>Modal Title</h2>
      <p>This is rendered at the body level.</p>
    </div>
  </div>
</Teleport>

<!-- Conditional teleport -->
<Teleport to="#modals" :disabled="inline">
  <div class="toast">Notification</div>
</Teleport>

Transition / TransitionGroup

Vue 3

Apply enter/leave animations to elements and lists.

<!-- Single element -->
<Transition name="fade">
  <p v-if="show">Hello</p>
</Transition>

<style>
.fade-enter-active, .fade-leave-active {
  transition: opacity 0.3s ease;
}
.fade-enter-from, .fade-leave-to {
  opacity: 0;
}
</style>

<!-- List transitions -->
<TransitionGroup name="list" tag="ul">
  <li v-for="item in items" :key="item.id">
    {{ item.text }}
  </li>
</TransitionGroup>

KeepAlive

Vue 3

Cache component instances when switching between dynamic components.

<!-- Cache all dynamic components -->
<KeepAlive>
  <component :is="currentView" />
</KeepAlive>

<!-- Cache specific components -->
<KeepAlive include="SearchPanel,ResultsList">
  <component :is="activePanel" />
</KeepAlive>

<!-- Limit cached instances -->
<KeepAlive :max="5">
  <component :is="currentTab" />
</KeepAlive>

<!-- Lifecycle hooks for cached components -->
<script setup>
import { onActivated, onDeactivated } from 'vue'

onActivated(() => { /* component brought back */ })
onDeactivated(() => { /* component cached */ })
</script>

Suspense

Vue 3

Display fallback content while waiting for async components.

<Suspense>
  <!-- Main content (async component) -->
  <template #default>
    <AsyncDashboard />
  </template>

  <!-- Loading fallback -->
  <template #fallback>
    <div class="loading">Loading dashboard...</div>
  </template>
</Suspense>

<!-- Async component with top-level await -->
<script setup>
const data = await fetch('/api/data').then(r => r.json())
</script>

<template>
  <div>{{ data.title }}</div>
</template>

Get More Developer Resources

Join our newsletter for cheat sheets, code snippets, and developer tools delivered weekly.

or use email

Get this + weekly PM tools and templates.