Cheat sheet · Vue 3.5
Vue 3 Composition API cheat sheet
ref, reactive, computed and watch, lifecycle hooks, props and emits, defineModel, composables, provide/inject and the script setup macros, for Vue 3.5 with TypeScript.
Checked against the official docs on 2026-10-15
Reactive state
ref and reactive
typescript
import { ref, reactive, shallowRef, toRefs, readonly } from 'vue'
const count = ref(0) // count.value in script, {{ count }} in template
const user = ref<User | null>(null)
const state = reactive({ items: [] as Item[], filter: '' }) // objects only
const { filter } = toRefs(state) // destructure without losing reactivity
const big = shallowRef(hugeList) // only .value is tracked
const locked = readonly(state)computed
typescript
const total = computed(() => cart.items.reduce((s, i) => s + i.price, 0))
const fullName = computed({
get: () => `${first.value} ${last.value}`,
set: (v) => { [first.value, last.value] = v.split(' ') },
})Watchers
watch and watchEffect
typescript
watch(query, (now, before) => search(now))
watch([a, () => state.b], ([a, b]) => { /* ... */ })
watch(() => state.filter, run, { immediate: true, once: true })
watch(state, save, { deep: true }) // deep: number limits depth (3.5)
watchEffect(() => console.log(count.value)) // runs now, tracks what it reads
watch(id, async (id) => {
const controller = new AbortController()
onWatcherCleanup(() => controller.abort()) // 3.5
data.value = await fetch(`/api/${id}`, { signal: controller.signal }).then((r) => r.json())
})
const stop = watch(src, cb); stop() // also pause() / resume()Lifecycle
Hooks
typescript
onBeforeMount(() => {})
onMounted(() => el.value?.focus()) // DOM is ready
onBeforeUpdate(() => {}); onUpdated(() => {})
onBeforeUnmount(() => {}); onUnmounted(() => clearInterval(timer))
onErrorCaptured((err) => false) // stop propagation
onActivated(() => {}); onDeactivated(() => {}) // <KeepAlive>
await nextTick() // after the DOM updatesProps and emits
Typed, with defaults (3.5 destructure)
vue
<script setup lang="ts">
const { title, size = 'md' } = defineProps<{
title: string
size?: 'sm' | 'md' | 'lg'
}>()
const emit = defineEmits<{
change: [id: number]
update: [value: string]
}>()
emit('change', 1)
</script>Destructured props stay reactive in 3.5; pass them to watch as a getter: watch(() => size, ...).
defineModel (v-model on components)
Child and parent
vue
<!-- Child.vue -->
<script setup lang="ts">
const model = defineModel<string>({ required: true })
const count = defineModel<number>('count', { default: 0 })
const [text, modifiers] = defineModel<string, 'trim'>({
set: (v) => (modifiers.trim ? v.trim() : v),
})
</script>
<template><input v-model="model"></template>
<!-- Parent.vue -->
<Child v-model="name" v-model:count="n" />script setup macros and template refs
Everything at the top level is in the template
vue
<script setup lang="ts">
import { useTemplateRef, useId, useAttrs, useSlots } from 'vue'
defineOptions({ inheritAttrs: false })
const input = useTemplateRef<HTMLInputElement>('input') // 3.5
const id = useId() // SSR-safe id (3.5)
const slots = defineSlots<{ default(props: { item: Item }): any }>()
defineExpose({ focus: () => input.value?.focus() })
</script>
<template><label :for="id">Name</label><input :id="id" ref="input"></template>Composables
useX: state + logic you can reuse
typescript
// composables/useFetch.ts
import { ref, toValue, watchEffect, type MaybeRefOrGetter } from 'vue'
export function useFetch<T>(url: MaybeRefOrGetter<string>) {
const data = ref<T | null>(null)
const error = ref<unknown>(null)
watchEffect(async () => {
data.value = null; error.value = null
try { data.value = await (await fetch(toValue(url))).json() }
catch (e) { error.value = e }
})
return { data, error } // return refs, not reactive()
}
const { data } = useFetch<User[]>(() => `/api/users?page=${page.value}`)provide / inject
Typed with an InjectionKey
typescript
// keys.ts
export const ThemeKey: InjectionKey<Ref<'light' | 'dark'>> = Symbol('theme')
// Ancestor
const theme = ref<'light' | 'dark'>('light')
provide(ThemeKey, theme) // provide readonly(theme) to block writes
app.provide('apiBase', '/api') // app-wide
// Any descendant
const theme = inject(ThemeKey) // Ref | undefined
const base = inject('apiBase', '/api') // with defaultSources: Reactivity API: Core · <script setup> · Component v-model · Composables
Free PDF
Get the Vue 3 Composition API cheat sheet as a printable PDF
A4, two pages, same content as this page. Join the monthly Laravel & Vue newsletter and the download link appears right here.
You're on the list. Here is your cheat sheet:
Download the PDF