TestForge | Aidevops | 📊 Plogger ✍️ Blog 📚 Docs
plogger

AI DevOps Korea

Turn AI service development and operations into one improvement loop

Aidevops.kr covers LLMOps, RAG, agents, observability, evaluation, and cost-performance optimization for production AI services.

Vue 3 Composition API Complete Guide

· Updated Apr 16
Vue 3 Composition API Complete Guide diagram
Visual guide to the key flow, architecture, and decision points covered in this post.
Vue 3's Composition API is not just a syntax change. It is a design tool that lets you break component logic into smaller units and recombine them. In production code, what matters is not memorizing the names `ref`, `reactive`, and `computed`, but understanding how far a piece of logic should be extracted into a composable.

Why Composition API matters

The Options API is easy to read in small components, but as components grow, related logic gets scattered across data, methods, watch, and computed. The Composition API lets you group that logic by feature.

In other words, its first real benefit is not reuse. It is cohesion.

setup() is the entry point, not the place for everything

import { ref, computed } from 'vue';

export default {
  setup() {
    const count = ref(0);
    const double = computed(() => count.value * 2);

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

    return { count, double, increment };
  },
};

At first, it is easy to dump all code into setup(), but in real projects it quickly turns into a giant function. That is usually the point where you should start extracting composables.

ref and reactive serve different roles

  • ref: a single value with a clear state boundary
  • reactive: an object whose fields move together

In practice, using ref as the default is usually more predictable, and using reactive only when multiple fields semantically belong to one object works better. reactive is convenient, but destructuring and dependency tracking boundaries can become blurry.

A composable is not just a reusable function, but a feature module

import { ref, onMounted } from 'vue';

export function useUserProfile(userId) {
  const profile = ref(null);
  const loading = ref(false);
  const error = ref(null);

  async function fetchProfile() {
    loading.value = true;
    error.value = null;
    try {
      const res = await fetch(`/api/users/${userId}`);
      profile.value = await res.json();
    } catch (e) {
      error.value = e;
    } finally {
      loading.value = false;
    }
  }

  onMounted(fetchProfile);

  return { profile, loading, error, fetchProfile };
}

A good composable usually encapsulates one feature, such as authentication, a user profile, pagination, or scroll state. That lets UI components focus on presentation while the logic lives in composables.

watch is powerful, but easy to overuse

In the Composition API, watch is useful when you need to react to external changes, but it can also make state flow harder to follow. If something can be expressed with computed, prefer computed first, and use watch only when you really need side effects.

Common problems in real projects

  • setup() grows so large that it becomes harder to read
  • one composable takes on networking, state, routing, and UI all at once
  • passing reactive objects around too freely and losing tracking boundaries
  • handling too much logic directly inside templates

The Composition API gives you a lot of freedom, and without structure that freedom can quickly turn into disorder.

  • group screen logic into composables
  • separate UI presentation state from business state
  • keep composable naming consistent with the useXxx pattern
  • encapsulate external API calls inside composables, but avoid over-generalizing them
  • start small and extract only when repeated logic actually appears

Wrap-up

The point of the Composition API is not to use more features. It is to place logic in a more cohesive structure. setup() is only the starting point, and real implementation quality depends on how well you define composable boundaries. If you get that right, Vue components become much easier to read, more reusable, and far more resilient to change.

What Gets Hard in Production

  • Composition API scales well, but only if composables have clear ownership and are not used as a universal dumping ground.
  • Cross-cutting reactive state can become hard to trace when composables implicitly share module-level state.
  • Reactivity is powerful enough to hide coupling until the app is large and change behavior becomes surprising.

Architecture Decisions That Matter

  • Write composables around stable responsibilities such as pagination, auth session access, or form orchestration.
  • Make it explicit whether a composable returns isolated state per caller or shared singleton state.
  • Keep side effects visible and close to the consumer whenever possible.

Practical Example

A reliable composable exposes one coherent capability and a deliberate state shape:

export function usePagination(initialPage = 1) {
  const page = ref(initialPage)
  const pageSize = ref(20)

  function next() {
    page.value += 1
  }

  return { page, pageSize, next }
}

Anti-Patterns to Avoid

  • Moving page-specific logic into a composable just to make files shorter.
  • Returning huge reactive bags with unclear invariants.
  • Sharing module-level refs without documenting lifecycle and reset behavior.

Operational Checklist

  • Review composable naming and ownership periodically.
  • Test composables with multiple consumer instances when isolation matters.
  • Document any shared-state composables explicitly.
  • Watch for reactivity chains that make debugging update triggers difficult.

Final Judgment

The Composition API is strongest when composables create readable behavioral units. It becomes weak when “reusable” simply means “moved elsewhere.”

Continue Reading

Related posts

Next Path

Keep exploring this topic as a system