Vue.js Composition API
The content here is under the Attribution 4.0 International (CC BY 4.0) license
A comprehensive guide to Vue 3’s Composition API, its core concepts, patterns, and best practices. This guide draws from multiple sources including the VueSchool.io course by Daniel Kelly and official Vue documentation.
Why Use the Composition API?
The Composition API addresses fundamental limitations of traditional Vue options:
- Solves mixin issues: Avoids naming collisions and implicit dependencies
- Better TypeScript support: Enables full type inference and safety
Core Concepts: Setup & Composition
The Setup Function
The setup() function serves as the entry point for the Composition API. It can be declared in two ways:
-
<script setup>(Vue 3.2+) - No explicit return needed -
export default { setup() {} }- Must return reactive data
Key responsibilities:
- Receives
propsas the first argument - Receives
contextas the second argument - Returns data and functions exposed to the template
Reactivity Fundamentals
Understanding Ref vs. Reactive
Vue provides two ways to create reactive state, each with distinct purposes:
ref() - Universal reactivity wrapper
- Works with both primitive and non-primitive values
- Requires
.valueto access in JavaScript - Accessed directly in templates (Vue handles unwrapping)
- Supports variable reassignment
- Allows object destructuring without breaking reactivity
reactive() - Object-only reactivity
- No
.valuerequired - Direct property access and destructuring with
toRef - Cannot be reassigned (breaks reactivity)
- Only works with objects and arrays
Best Practices
- Copy props to local state: Avoid reference issues and maintain reactivity isolation
-
Use
const: Prevents accidental reactivity loss through reassignment -
Remember the distinction: Objects stored by reference, primitives need
ref()
Advanced Reactivity Utilities
-
isReadonly()- Check if value is read-only -
isRaw()- Check if value is a raw object -
markRaw()- Mark object to skip reactivity -
shallowReactive()- Only root-level reactivity -
shallowReadonly()- Only root-level read-only protection
Computed Properties
The computed() function creates derived reactive values with optional getter/setter:
const fullName = computed({
get: () => `${first.value} ${last.value}`,
set: (newValue) => {
// update first and last
}
})
Watchers: Tracking State Changes
watch() - Targeted Observation
Monitor specific reactive values and respond to changes:
- First argument: The variable to watch
-
Second argument: Callback function with
(newValue, oldValue) -
Options:
-
immediate: true- Fire callback on initial setup -
deep: true- Watch nested properties
-
- Cleanup: Call the return value to unsubscribe
Note: When watching arrays created with reactive() or ref(), create a copy to trigger properly.
watchEffect() - Automatic Dependency Tracking
Automatically registers all dependencies accessed inside the callback:
- Fires immediately on creation
- No access to previous value
- Ideal when all reactive values in the callback should be watched
Composition Patterns
Composables: Reusable Logic
Extract and share component logic through composables (Vue’s equivalent of React hooks):
- Defined outside components
- Encapsulate reactive state and methods
- Superior to mixins (avoids naming conflicts)
- Can compose multiple composables together
Vue Router Integration
Vue Router provides composables for accessing route state:
-
useRoute()- Access current route -
useRouter()- Navigate programmatically
Async Operations with Suspense (Experimental)
Handle async setup with <Suspense>:
<template>
<Suspense>
<template #default>
<AsyncComponent />
</template>
<template #fallback>
<div>Loading...</div>
</template>
</Suspense>
</template>
Avoiding Prop Drilling with Provide/Inject
Share data deeply nested components without passing through each level:
-
provide(key, value)- Offer data from parent -
inject(key, defaultValue)- Consume data in descendants - Useful for avoiding intermediate component prop chains
TypeScript Support in Vue 3
Vue 3 provides excellent TypeScript support, with concepts explored in depth in the TypeScript with Vue.js 3 course
Getting Started
Add lang="ts" to your <script> tag for full TypeScript support.
Typing Reactive State
- Use
reactive()with type annotations:reactive<MyType>({}) - Use
as MyTypefor editor hints on ref unwrapped values
Typing Props and Events
Props definition:
defineProps<{
prop1: string
}>()
Default values for props are still experimental.
Events:
- Use consistent symbols for event names like
@createfor IDE autocomplete
Injection with Types
Create a typed injection key:
const injectionKey = Symbol as InjectionKey<MyType>
// In parent
provide(injectionKey, value)
// In child
const value = inject(injectionKey)
Further Reading
- Official Vue 3 Documentation - Comprehensive reference
- Vue.js 3 By Example - Packt Publishing
- VueSchool.io Courses - Interactive learning platform