Vue.js logoVue.jsv3.5BEGINNER

Vue.js

Vue 3 cheat sheet covering Composition API, reactivity, components, directives, lifecycle hooks, and Pinia state management examples.

15 min read
vuecomposition-apioptions-apireactivecomponents

Sign in to mark items as known and track your progress.

Sign in

Getting Started

Vue 3 setup and basics

Creating a Vue 3 App

Different ways to create Vue 3 applications

javascript
// CDN (Vue 3)
<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
<div id="app">{{ message }}</div>
<script>
  const { createApp } = Vue;
  createApp({
    data() {
      return { message: 'Hello Vue!' }
    }
  }).mount('#app');
</script>

// Vite (Recommended for Vue 3)
npm create vue@latest my-app
cd my-app
npm install
npm run dev
💡 Vue 3 is the current version, Vue 2 reached EOL in Dec 2023
⚡ Vite is the recommended build tool for Vue 3 projects
📌 Use create-vue for the best Vue 3 setup experience
🔥 CDN version is only for prototyping, not production

Vue 3 Composition API Setup

Script setup syntax (Vue 3 recommended way)

vue
<template>
  <div>{{ message }}</div>
  <button @click="increment">{{ count }}</button>
</template>

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

const message = ref('Hello')
const count = ref(0)

const doubled = computed(() => count.value * 2)

function increment() {
  count.value++
}
</script>
💡 Script setup is the recommended syntax for Vue 3 Composition API
⚡ No need to return variables from setup() with script setup
📌 defineProps and defineEmits are compiler macros, no import needed
🔥 Vue 3 allows multiple root elements in templates

Directives

Vue built-in directives

Conditional Rendering

v-if, v-else, v-show directives

vue
<!-- v-if/v-else (Vue 2 & 3) -->
<div v-if="type === 'A'">A</div>
<div v-else-if="type === 'B'">B</div>
<div v-else>Not A or B</div>

<!-- v-show (Vue 2 & 3) -->
<div v-show="isVisible">Always in DOM</div>

<!-- Template v-if (Vue 2 & 3) -->
<template v-if="ok">
  <h1>Title</h1>
  <p>Paragraph</p>
</template>
💡 v-if removes/adds elements to DOM, v-show toggles CSS display
⚡ Use v-show for frequent toggling, v-if for rare changes
📌 In Vue 3, v-if has higher priority than v-for (opposite of Vue 2)
🔥 template tag renders no wrapper element, useful for grouping

List Rendering

v-for directive for rendering lists

vue
<!-- Array iteration (Vue 2 & 3) -->
<li v-for="item in items" :key="item.id">
  {{ item.text }}
</li>

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

<!-- Object iteration -->
<div v-for="(value, key) in object" :key="key">
  {{ key }}: {{ value }}
</div>
💡 Always use :key with v-for for efficient updates
⚡ Use stable, unique IDs for keys, not array indexes
📌 Vue 3 has full array reactivity, Vue 2 has limitations
🔥 v-for can iterate arrays, objects, ranges, and components

Event Handling & Input Binding

v-on (@) and v-model directives

vue
<!-- Event handling (Vue 2 & 3) -->
<button @click="handleClick">Click</button>
<button @click="count++">Count: {{ count }}</button>

<!-- v-model (Vue 2 & 3) -->
<input v-model="message" />
<textarea v-model="text"></textarea>
<select v-model="selected">
  <option>A</option>
  <option>B</option>
</select>

<!-- Modifiers -->
<input v-model.trim="text" />
<input v-model.number="age" />
<form @submit.prevent="onSubmit">
💡 @ is shorthand for v-on, : is shorthand for v-bind
⚡ Event modifiers can be chained: @click.stop.prevent
📌 v-model is two-way binding, equivalent to :value + @input
🔥 Vue 3 supports multiple v-model bindings on components

Attribute Binding

v-bind (:) directive and dynamic attributes

vue
<!-- v-bind (Vue 2 & 3) -->
<img :src="imageSrc" :alt="imageAlt" />
<div :class="{ active: isActive }"></div>
<div :style="{ color: textColor }"></div>

<!-- Dynamic attributes (Vue 2 & 3) -->
<div :[attributeName]="value"></div>

<!-- Multiple bindings -->
<div v-bind="objectOfAttrs"></div>
💡 : is shorthand for v-bind, binds expressions to attributes
⚡ Class and style bindings merge with static values
📌 v-bind without argument binds entire object as attributes
🔥 Dynamic attribute names allow runtime attribute changes

Computed Properties & Watchers

Reactive computations and side effects

Computed Properties

Cached reactive computations

vue
<!-- Vue 3 Composition API -->
<script setup>
import { ref, computed } from 'vue'

const price = ref(100)
const quantity = ref(2)

const total = computed(() => price.value * quantity.value)
const formattedTotal = computed(() => `$${total.value}`)
</script>

<!-- Vue 2/3 Options API -->
<script>
export default {
  data() {
    return { price: 100, quantity: 2 }
  },
  computed: {
    total() {
      return this.price * this.quantity
    }
  }
}
</script>
💡 Computed properties are cached based on reactive dependencies
⚡ Use computed for expensive operations that depend on reactive data
📌 Computed properties are read-only by default, use get/set for writable
🔥 In Vue 3, computed returns a ref, access with .value in script

Watchers

Reacting to data changes with side effects

vue
<!-- Vue 3 Composition API -->
<script setup>
import { ref, watch, watchEffect } from 'vue'

const count = ref(0)

// Watch single ref
watch(count, (newVal, oldVal) => {
  console.log(`Count changed: ${oldVal} -> ${newVal}`)
})

// watchEffect runs immediately
watchEffect(() => {
  console.log(`Count is: ${count.value}`)
})
</script>
💡 watchEffect runs immediately, watch runs only on change
⚡ Use deep: true to watch nested object changes
📌 watchEffect auto-tracks dependencies, watch is explicit
🔥 Return value from watch/watchEffect stops the watcher

Refs & Reactive State

Vue 3 reactivity system

Refs and Reactive

Creating reactive state in Vue 3

vue
<!-- Vue 3 Composition API -->
<script setup>
import { ref, reactive, toRefs, toRef } from 'vue'

// ref for primitives
const count = ref(0)
const message = ref('Hello')

// reactive for objects
const state = reactive({
  user: { name: 'John' },
  items: []
})

// Accessing/modifying
count.value++
state.user.name = 'Jane'
</script>
💡 Use ref() for primitives, reactive() for objects in Vue 3
⚡ Refs auto-unwrap in templates, need .value in script
📌 toRefs() lets you destructure reactive objects without losing reactivity
🔥 Template refs give access to DOM elements or component instances

Component Communication

Props, events, and provide/inject

Props

Passing data to child components

vue
<!-- Vue 3 Script Setup -->
<script setup>
// Define props
const props = defineProps({
  title: String,
  count: {
    type: Number,
    default: 0,
    required: true,
    validator: (value) => value >= 0
  }
})

// Use props
console.log(props.title)
</script>
💡 Props are read-only, child should not mutate them
⚡ Use factory functions for object/array default values
📌 HTML attributes are case-insensitive, use kebab-case in templates
🔥 v-bind without argument passes all properties as props

Custom Events

Child to parent communication

vue
<!-- Vue 3 Child Component -->
<script setup>
const emit = defineEmits(['update', 'delete'])

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

<!-- Parent listens -->
<ChildComponent 
  @update="handleUpdate"
  @delete="handleDelete"
/>
💡 Events bubble up from child to parent components
⚡ Event names should be kebab-case in templates
📌 Vue 3 allows event validation and multiple v-model
🔥 Use update:propName pattern for v-model compatibility

Provide/Inject

Dependency injection for deeply nested components

vue
<!-- Vue 3 Provider -->
<script setup>
import { provide, ref } from 'vue'

const user = ref({ name: 'John' })
provide('user', user)
provide('theme', 'dark')
</script>

<!-- Vue 3 Consumer -->
<script setup>
import { inject } from 'vue'

const user = inject('user')
const theme = inject('theme', 'light') // with default
</script>
💡 Provide/Inject allows passing data to deeply nested components
⚡ Injected values are not reactive in Vue 2 unless wrapped in function
📌 Use Symbol keys for type safety and avoiding collisions
🔥 Great for plugin systems and avoiding prop drilling

Forms & v-model

Form handling and two-way binding

Form Inputs

v-model with different input types

vue
<!-- Text inputs -->
<input v-model="message" />
<textarea v-model="text"></textarea>

<!-- Checkboxes -->
<input type="checkbox" v-model="checked" />
<input type="checkbox" v-model="checkedNames" value="John" />

<!-- Radio -->
<input type="radio" v-model="picked" value="One" />

<!-- Select -->
<select v-model="selected">
  <option>A</option>
  <option>B</option>
</select>
💡 v-model creates two-way binding on form inputs
⚡ Use .number modifier for numeric inputs, .trim for strings
📌 Multiple checkboxes bind to array, single to boolean
🔥 File inputs don't support v-model, use @change event

Form Validation

Implementing form validation

vue
<template>
  <form @submit.prevent="validateAndSubmit">
    <div>
      <input 
        v-model="form.email"
        @blur="validateEmail"
        :class="{ error: errors.email }"
      />
      <span v-if="errors.email">{{ errors.email }}</span>
    </div>
    
    <button :disabled="!isValid">Submit</button>
  </form>
</template>

<script setup>
import { reactive, computed } from 'vue'

const form = reactive({
  email: '',
  password: ''
})

const errors = reactive({
  email: '',
  password: ''
})

const isValid = computed(() => 
  !errors.email && !errors.password && 
  form.email && form.password
)

function validateEmail() {
  if (!form.email) {
    errors.email = 'Email is required'
  } else if (!/\S+@\S+\.\S+/.test(form.email)) {
    errors.email = 'Email is invalid'
  } else {
    errors.email = ''
  }
}
</script>
💡 Validate on blur for better UX, clear errors on input
⚡ Use computed properties for dynamic validation states
📌 Consider validation libraries like Vuelidate or VeeValidate
🔥 Always validate on both client and server side

Vue Router

Client-side routing in Vue

Router Setup

Setting up Vue Router

javascript
// router/index.js (Vue 3)
import { createRouter, createWebHistory } from 'vue-router'

const routes = [
  { path: '/', component: Home },
  { path: '/about', component: About },
  { path: '/user/:id', component: User }
]

const router = createRouter({
  history: createWebHistory(),
  routes
})

// main.js
app.use(router)
💡 Vue Router 4 for Vue 3, Vue Router 3 for Vue 2
⚡ Use lazy loading with import() for better performance
📌 History mode requires server configuration for production
🔥 Navigation guards control access to routes

Navigation

Navigating between routes

vue
<!-- Template navigation -->
<router-link to="/">Home</router-link>
<router-link :to="{ name: 'user', params: { id: 123 }}">
  User
</router-link>

<!-- Programmatic navigation -->
<script setup>
import { useRouter } from 'vue-router'
const router = useRouter()

router.push('/about')
router.push({ name: 'user', params: { id: 123 }})
</script>
💡 router-link renders as <a> tag with proper href
⚡ Use named routes to avoid hardcoding paths
📌 router.push adds to history, router.replace doesn't
🔥 Route params are strings, parse numbers when needed

State Management (Pinia)

Global state management with Pinia (Vue 3)

Pinia Store

Creating and using Pinia stores

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

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

// Using in component
<script setup>
const counter = useCounterStore()
counter.increment()
</script>
💡 Pinia is the official state management for Vue 3
⚡ Setup syntax is more flexible but Option syntax is clearer
📌 Use storeToRefs() to destructure state with reactivity
🔥 Stores can use other stores and share logic easily

Lifecycle Hooks

Component lifecycle management

Vue 3 Lifecycle

Composition API lifecycle hooks

vue
<script setup>
import { 
  onMounted, 
  onUpdated, 
  onUnmounted,
  onBeforeMount,
  onBeforeUpdate,
  onBeforeUnmount
} from 'vue'

onMounted(() => {
  console.log('Component mounted')
})

onUnmounted(() => {
  console.log('Component unmounted')
})
</script>
💡 Setup runs before all lifecycle hooks in Composition API
⚡ Use mounted for DOM access, created/setup for data init
📌 Always cleanup in unmounted (timers, listeners, subscriptions)
🔥 Keep-alive hooks only work with <keep-alive> wrapper

Advanced Features

Vue 3 advanced features

Slots

Content distribution with slots

vue
<!-- Parent -->
<Card>
  <template #header>
    <h1>Title</h1>
  </template>
  
  Default slot content
  
  <template #footer>
    <p>Footer</p>
  </template>
</Card>

<!-- Child (Card.vue) -->
<template>
  <div class="card">
    <header><slot name="header"></slot></header>
    <main><slot></slot></main>
    <footer><slot name="footer"></slot></footer>
  </div>
</template>
💡 Slots enable flexible component composition
⚡ Scoped slots pass data from child to parent
📌 Use # as shorthand for v-slot:
🔥 $slots object contains all passed slots

Teleport & Suspense

Vue 3 Teleport and Suspense features

vue
<!-- Teleport -->
<Teleport to="body">
  <div class="modal">
    Modal content
  </div>
</Teleport>

<!-- Suspense -->
<Suspense>
  <AsyncComponent />
  <template #fallback>
    <div>Loading...</div>
  </template>
</Suspense>
💡 Teleport renders content outside component hierarchy
⚡ Suspense handles async component loading states
📌 Top-level await in setup makes component async
🔥 Combine Suspense with error boundaries for complete async handling

Composables

Reusable composition functions

javascript
// composables/useMouse.js
import { ref, onMounted, onUnmounted } from 'vue'

export function useMouse() {
  const x = ref(0)
  const y = ref(0)
  
  function update(e) {
    x.value = e.pageX
    y.value = e.pageY
  }
  
  onMounted(() => window.addEventListener('mousemove', update))
  onUnmounted(() => window.removeEventListener('mousemove', update))
  
  return { x, y }
}

// Using composable
<script setup>
import { useMouse } from './composables/useMouse'
const { x, y } = useMouse()
</script>
💡 Composables extract and reuse stateful logic
⚡ Convention: composable names start with "use"
📌 Composables can only be called in setup or other composables
🔥 Return refs to maintain reactivity when destructuring

Transitions & Animations

Adding transitions and animations

Transition Component

Single element/component transitions

vue
<!-- Basic transition -->
<Transition name="fade">
  <p v-if="show">Hello</p>
</Transition>

<!-- CSS -->
<style>
.fade-enter-active, .fade-leave-active {
  transition: opacity 0.5s;
}
.fade-enter-from, .fade-leave-to {
  opacity: 0;
}
</style>
💡 Vue 3 uses -from suffix instead of Vue 2's bare class names
⚡ Use mode="out-in" for smooth component transitions
📌 :css="false" for JavaScript-only transitions
🔥 TransitionGroup for animating lists with automatic move transitions