intermediate Step 11 of 15

Composition API

Vue.js Development

Composition API

The Composition API is Vue 3's alternative to the Options API for organizing component logic. Instead of splitting code across data, computed, methods, and watch options, the Composition API lets you group related logic together using composable functions. This is especially valuable in large components where a single feature's code is scattered across multiple options. The Composition API uses functions like ref, reactive, computed, and watch imported from Vue, and organizes code in the setup() function or the <script setup> syntax.

ref and reactive

The ref function creates a reactive reference for primitive values, while reactive creates a reactive proxy for objects. Both trigger re-renders when their values change.

<template>
  <div>
    <p>Count: {{ count }}</p>
    <p>User: {{ user.name }} ({{ user.age }})</p>
    <button @click="increment">Increment</button>
    <button @click="updateUser">Birthday</button>
  </div>
</template>

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

// ref for primitives — access value with .value in script
const count = ref(0);
const message = ref("Hello Vue 3!");

// reactive for objects — no .value needed
const user = reactive({
  name: "Alice",
  age: 25,
  hobbies: ["reading", "coding"],
});

function increment() {
  count.value++; // .value needed in script
}

function updateUser() {
  user.age++; // direct access, no .value
}
</script>

computed and watch

The Composition API versions of computed properties and watchers are imported as functions. They work similarly to their Options API counterparts but are defined alongside the data they depend on.

<script setup>
import { ref, computed, watch, watchEffect } from "vue";

const items = ref([
  { id: 1, name: "Apple", price: 1.5, quantity: 3 },
  { id: 2, name: "Banana", price: 0.5, quantity: 6 },
  { id: 3, name: "Cherry", price: 3.0, quantity: 2 },
]);

const searchQuery = ref("");

// Computed — cached, reactive derived value
const filteredItems = computed(() => {
  const q = searchQuery.value.toLowerCase();
  if (!q) return items.value;
  return items.value.filter((item) =>
    item.name.toLowerCase().includes(q)
  );
});

const totalCost = computed(() =>
  items.value.reduce((sum, item) => sum + item.price * item.quantity, 0)
);

// watch — observe specific source, get old and new values
watch(searchQuery, (newVal, oldVal) => {
  console.log(`Search changed: "${oldVal}" -> "${newVal}"`);
});

// watch with options
watch(
  () => items.value.length,
  (newLen) => {
    console.log(`Item count changed to ${newLen}`);
  },
  { immediate: true }
);

// watchEffect — auto-tracks all reactive deps used inside
watchEffect(() => {
  console.log(`Showing ${filteredItems.value.length} items, total: $${totalCost.value}`);
});
</script>

Composable Functions

Composables are the Composition API's answer to mixins and higher-order components. They are plain functions that encapsulate and reuse stateful logic across components.

// composables/useMouse.js
import { ref, onMounted, onUnmounted } from "vue";

export function useMouse() {
  const x = ref(0);
  const y = ref(0);

  function update(event) {
    x.value = event.clientX;
    y.value = event.clientY;
  }

  onMounted(() => window.addEventListener("mousemove", update));
  onUnmounted(() => window.removeEventListener("mousemove", update));

  return { x, y };
}

// composables/useFetch.js
import { ref, watchEffect } from "vue";

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

  watchEffect(async () => {
    loading.value = true;
    error.value = null;
    try {
      const response = await fetch(url.value || url);
      data.value = await response.json();
    } catch (e) {
      error.value = e.message;
    } finally {
      loading.value = false;
    }
  });

  return { data, error, loading };
}

// Usage in a component
// <script setup>
// import { useMouse } from "../composables/useMouse";
// import { useFetch } from "../composables/useFetch";
//
// const { x, y } = useMouse();
// const { data: users, loading, error } = useFetch("/api/users");
// </script>
Tip: The <script setup> syntax is the recommended way to use the Composition API. It reduces boilerplate by auto-exposing top-level bindings to the template and supports defineProps, defineEmits, and defineExpose compiler macros.

Key Takeaways

  • ref creates reactive references for primitives (access with .value in script).
  • reactive creates reactive proxies for objects (direct property access).
  • computed and watch are imported as functions and defined alongside related logic.
  • Composable functions encapsulate reusable stateful logic across components.
  • <script setup> is the recommended syntax for Composition API components.