Vue 3 Composition API: Patterns That Scale

The Composition API is more flexible than the Options API but easier to misuse. Here are the patterns that hold up as components grow.

Richard GamoraRichard GamoraFullstack developer·5 min read
VueComposition APIJavaScript

The Composition API is what I reach for in any new Vue 3 component. It is more flexible than the Options API, but the flexibility is also where teams trip up. Here are the patterns that have held up across real projects.

Group by feature, not by lifecycle

The big win of the Composition API is that related code lives together. Instead of state at the top, methods in the middle, and lifecycle at the bottom (Options API style), you put all the search logic together, all the form logic together, all the modal logic together. The component reads like sections, not phases.

Extract composables once a pattern repeats

Do not extract a composable on the first usage. Wait for the second or third. Once you have three components doing roughly the same thing — fetching paged data, managing a wizard, debouncing input — pull a useX() composable that takes the variations as arguments.

Premature composables tend to be misshaped. They have too many options, too many return values, or hardcode assumptions that the second use case breaks. Wait for the pattern to settle.

Refs vs reactive

Use ref() for primitives and single objects you might replace. Use reactive() for grouped state that you update piecemeal. The most common pitfall is destructuring a reactive object — that breaks reactivity. With ref(), you have to remember .value, but you can pass the ref around safely.

When in doubt, use ref(). It is the more general primitive and works for everything.

Watching what you actually need

watch() and watchEffect() do similar jobs but trigger differently. watchEffect() runs immediately and tracks any reactive dependency it touches. watch() is explicit — you specify what to watch and what to do.

Use watch() when the trigger needs to be precise. Use watchEffect() when the dependencies will change as the code evolves and you do not want to maintain the dependency list. Both have a place.

About the author

Richard Gamora

Richard Gamora

Fullstack developer based in the Philippines, working mostly with Laravel and Vue.js, with eight years of production experience across web and mobile.

me@richardgamora.comUpwork ↗

More on Vue & Nuxt