• Fri, Mar 2026

Vue 3 Composition API Explained for Advanced Developers

Vue 3 Composition API Explained for Advanced Developers

The Vue 3 Composition API is not just a fancy alternative to the Options API—it’s a paradigm shift that allows advanced developers to build scalable, maintainable, and reusable logic in modern Vue applications.In this tutorial, we’ll dive deep into the Composition API with hands-on code, advanced concepts, and a complete real-world example that ties everything together.

Introduction

If you’ve worked with Vue for a while, you know the Options API: data, methods, computed, lifecycle hooks. It’s simple, beginner-friendly, but once your app grows, things get messy. Related logic spreads across multiple options, and reusing functionality is often painful.

Enter the Composition API. Instead of scattering code across options, you group logic by functionality using reactive primitives like ref, reactive, and computed. The result? Clean, modular, and highly reusable Vue code.

Step 1: Setting Up Vue 3 with Composition API

Good news: the Composition API is built into Vue 3. If you scaffold a project with npm init vue@latest or vue-cli, you’re good to go. You’ll use <script setup> for concise syntax.

Example Starter

<script setup>
import { ref } from "vue";

const message = ref("Hello, Composition API!");
</script>

<template>
  <h2>{{ message }}</h2>
</template>

Here, ref creates a reactive reference. Unlike plain variables, changing message.value automatically updates the UI.

Step 2: Understanding Reactive Primitives

ref()

ref() wraps primitive values (strings, numbers, booleans) into reactive objects.

import { ref } from "vue";

const counter = ref(0);

function increment() {
  counter.value++;
}

reactive()

reactive() is best for objects and arrays. It returns a proxy that tracks nested properties.

import { reactive } from "vue";

const user = reactive({
  name: "Alice",
  age: 25
});

user.age++; // reactive update

computed()

computed() creates derived values that automatically recalculate when dependencies change.

import { computed, ref } from "vue";

const firstName = ref("John");
const lastName = ref("Doe");

const fullName = computed(() => `${firstName.value} ${lastName.value}`);

Step 3: Lifecycle Hooks in Composition API

Instead of created() or mounted(), you use imported hooks.

import { onMounted, onUnmounted } from "vue";

onMounted(() => {
  console.log("Component mounted!");
});

onUnmounted(() => {
  console.log("Component destroyed!");
});

Hooks can be used anywhere inside <script setup>, making them easier to organize by logic rather than component structure.

Step 4: Creating Reusable Composables

One of the biggest wins of the Composition API is composables—functions that encapsulate reactive logic for reuse.

Example: useCounter.js

// composables/useCounter.js
import { ref } from "vue";

export function useCounter(initial = 0) {
  const count = ref(initial);

  function increment() {
    count.value++;
  }

  function decrement() {
    count.value--;
  }

  return { count, increment, decrement };
}

Using the Composable

<script setup>
import { useCounter } from "./composables/useCounter";

const { count, increment, decrement } = useCounter(10);
</script>

<template>
  <div>
    <p>Count: {{ count }}</p>
    <button @click="increment">+</button>
    <button @click="decrement">-</button>
  </div>
</template>

Now you can reuse this useCounter composable across multiple components.

Step 5: Advanced Reactivity Patterns

watch()

Use watch to react to changes in reactive values.

import { ref, watch } from "vue";

const search = ref("");

watch(search, (newVal, oldVal) => {
  console.log("Search changed:", oldVal, "→", newVal);
});

watchEffect()

watchEffect runs immediately and tracks dependencies automatically.

import { ref, watchEffect } from "vue";

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

watchEffect(() => {
  console.log("Total:", price.value * quantity.value);
});

shallowRef and shallowReactive

Sometimes you don’t want deep reactivity—for performance reasons. That’s where shallowRef and shallowReactive come in.

Step 6: Real-World Example — Todo App with Composition API

Let’s build a small but real-world Todo app using the Composition API. This example covers reactivity, computed properties, composables, and persistence with Local Storage.

composables/useTodos.js

import { ref, watch } from "vue";

export function useTodos() {
  const todos = ref(JSON.parse(localStorage.getItem("todos") || "[]"));
  const newTodo = ref("");

  function addTodo() {
    if (newTodo.value.trim() === "") return;
    todos.value.push({ text: newTodo.value, completed: false });
    newTodo.value = "";
  }

  function toggleTodo(index) {
    todos.value[index].completed = !todos.value[index].completed;
  }

  function removeTodo(index) {
    todos.value.splice(index, 1);
  }

  watch(todos, (val) => {
    localStorage.setItem("todos", JSON.stringify(val));
  }, { deep: true });

  return { todos, newTodo, addTodo, toggleTodo, removeTodo };
}

TodoApp.vue

<script setup>
import { useTodos } from "./composables/useTodos";

const { todos, newTodo, addTodo, toggleTodo, removeTodo } = useTodos();
</script>

<template>
 <div>
  <h1>My Todos</h1>
  <input v-model="newTodo" placeholder="Add todo" @keyup.enter="addTodo" />
  <button @click="addTodo">Add</button>
  <ul>
   <li v-for="(todo, index) in todos" :key="index">
    <input type="checkbox" v-model="todo.completed" @change="toggleTodo(index)" />
    <span :>
     {{ todo.text }}
    </span>
    <button @click="removeTodo(index)">Delete</button>
   </li>
  </ul>
 </div>
</template>

This Todo app is small but powerful—it persists todos in localStorage, demonstrates reactivity, and organizes logic in a composable for reuse.

Step 7: Best Practices with Composition API

  • Group by feature, not type: Keep related logic (state, functions, watchers) together.
  • Use composables liberally: They’re the key to code reuse and separation of concerns.
  • Prefer ref for primitives, reactive for objects.
  • Be mindful of performance: Shallow reactivity and watchEffect are advanced tools for optimization.

Conclusion

The Vue 3 Composition API is a game changer for advanced developers. It brings flexibility, better code organization, and powerful reusability. With reactive primitives, composables, and advanced patterns, you can build apps that are easier to maintain and scale.

Whether you’re building a Todo app or a large-scale enterprise dashboard, the Composition API will be your best companion. Now, take this knowledge and refactor one of your existing projects—you’ll feel the difference immediately.

This website uses cookies to enhance your browsing experience. By continuing to use this site, you consent to the use of cookies. Please review our Privacy Policy for more information on how we handle your data. Cookie Policy