← Back to Blogs

Vue.js 3 Composition API — A Practical Introduction

January 18, 2026 · 9 min read

Vue.js 3 Composition API JavaScript TypeScript Frontend

Introduction

Vue.js 3 introduced the Composition API as a powerful alternative to the traditional Options API. It addresses several long-standing pain points — logic reuse across components, better organization of complex features, and improved TypeScript support. In this practical guide, we will explore the core concepts of the Composition API and build reusable composables that you can drop into your next project.

Why the Composition API?

The Options API organizes component logic into data, computed, methods, watch, and lifecycle hooks. While intuitive for small components, this vertical split scatters related logic across different options. When a component grows, you end up scrolling between data, computed, and methods to understand a single feature. Mixins attempt to solve this but introduce name conflicts and unclear dependency chains. The Composition API lets you group logically related code together and extract reusable pieces as composable functions.

ref and reactive

The two fundamental reactivity primitives are ref() and reactive(). Use ref() for primitive values and reactive() for objects. A ref wraps the value in a reactive object with a .value property; in templates, Vue auto-unwraps it.

import { ref, reactive } from 'vue'

const count = ref(0)
const user = reactive({ name: 'Alice', age: 30 })

function increment() {
  count.value++
  user.age++
}

computed and watch

Derived state is expressed with computed(), which returns a read-only reactive ref. Side effects use watch() and watchEffect(). The watch function requires a specific source, while watchEffect automatically tracks all reactive dependencies accessed inside it.

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

const price = ref(100)
const quantity = ref(2)
const total = computed(() => price.value * quantity.value)

watch(total, (newVal, oldVal) => {
  console.log(`Total changed from ${oldVal} to ${newVal}`)
})

Lifecycle Hooks in Setup

Lifecycle hooks like onMounted, onUnmounted, and onBeforeUpdate are imported directly and called inside the setup() function or <script setup> block. This avoids the Options API's global hook naming and keeps setup-side effects close together.

Writing Custom Composables

A composable is a function that uses Vue's Composition API to encapsulate and return reactive state and methods. This is the primary mechanism for logic reuse in Vue 3. Below are two practical examples.

useCounter

// composables/useCounter.js
import { ref, computed } from 'vue'

export function useCounter(initialValue = 0) {
  const count = ref(initialValue)
  const double = computed(() => count.value * 2)

  function increment() { count.value++ }
  function decrement() { count.value-- }
  function reset()     { count.value = initialValue }

  return { count, double, increment, decrement, reset }
}

useFetch

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

export function useFetch(url) {
  const data = ref(null)
  const error = ref(null)
  const loading = ref(true)
  let controller = null

  async function execute() {
    controller = new AbortController()
    loading.value = true
    try {
      const res = await fetch(url, { signal: controller.signal })
      data.value = await res.json()
    } catch (e) {
      if (e.name !== 'AbortError') error.value = e.message
    } finally {
      loading.value = false
    }
  }

  onMounted(execute)
  onUnmounted(() => controller?.abort())

  return { data, error, loading, refresh: execute }
}

Composition vs Options API

The Options API remains perfectly valid in Vue 3 and is arguably more readable for small, simple components where logic fits neatly into the predefined options. Choose the Composition API when a component has multiple features sharing state, when you need to reuse logic across components, or when TypeScript integration is a priority. There is no rule that says you must pick one exclusively — mixing both in the same component is supported, though rarely recommended.

TypeScript Integration

The Composition API pairs exceptionally well with TypeScript. ref<Type>() and reactive() infer types automatically. Composable return types are fully preserved, making autocompletion and refactoring reliable. This is a significant improvement over the Options API, where this context often required complex type augmentation.

Conclusion

The Composition API is not a replacement for the Options API — it is a complementary tool that unlocks better organization, reuse, and type safety for complex components. By mastering ref, reactive, computed, watch, and custom composables, you are equipped to build scalable Vue.js applications. Start small: extract a single piece of logic into a composable and see how it simplifies your component.

Need expert Vue.js development for your next project? From SPAs to design systems, our team delivers production-ready frontends with Vue 3 and the Composition API.

Get in Touch