🚀 LEVEL UP TO SENIOR:Unlock 500+ Advanced Practical Challenges & Exercises.
🎓 COURSERA PARTNER:Earn professional Google, Meta, and IBM certificates to supercharge your resume.
HTML MASTER CLASS /// LEARN TAGS /// BUILD STRUCTURE /// SEMANTIC WEB /// HTML MASTER CLASS /// LEARN TAGS ///

Composables in Vue.js

Learn about Composables in this comprehensive Vue.js tutorial. Learn how to write and consume composables to keep your components small and your logic DRY (Don't Repeat Yourself).

Total XP: 0|💻 vuejs XP: 0

Skill Matrix

UNLOCK NODES BY LEARNING NEW TAGS.

Select an unlocked node to view details root

🚀 LEVEL UP TO SENIOR:Unlock 500+ Advanced Practical Challenges & Exercises.
🎓 COURSERA PARTNER:Earn professional Google, Meta, and IBM certificates to supercharge your resume.

Extract and reuse stateful logic across your app.

1What is a Composable?

In the context of Vue applications, a 'composable' is a function that leverages Vue's Composition API to encapsulate and reuse stateful logic. If you are familiar with React, this is identical in concept to a 'Custom Hook'.

2Conventions

Composables are standard JavaScript/TypeScript files (not .vue files). By convention, their names always start with use, such as useFetch or useDarkTheme. They typically return an object containing refs or functions.

3Why not just classes/mixins?

Vue 2 relied on Mixins for logic reuse, but mixins caused naming collisions and made it hard to trace where a variable came from. Composables use explicit imports and destructuring (const { x } = useMouse()), making the source of every variable 100% clear.

4Step-by-Step Breakdown

What happens when you have logic (like fetching user data, or tracking mouse position) that you want to reuse across MULTIPLE components?

In Vue 3, the answer is "Composables". A Composable is simply a standard JavaScript function that leverages Vue's Composition API (ref, onMounted, etc.).

What do we call a reusable JavaScript function that encapsulates stateful logic using Vue's Composition API (like ref and watch)?

  • A Mixin
  • A Component
  • A Composable

By convention, the names of Composables always start with "use". For example, useFetch, useMouse, useTheme.

Inside the composable, you can use lifecycle hooks just like you would inside a component!

Is it possible to use Vue lifecycle hooks like onMounted and onUnmounted inside a standard JavaScript function (Composable) outside of a .vue file?

  • No, hooks only work in .vue files
  • Yes, as long as it's called synchronously inside setup()

To use the Composable in your component, you just import it and call it inside <script setup>. The component gets the reactive variables!

Composables are the modern replacement for Vue 2 "Mixins". They solve all the problems of mixins (naming collisions, hidden sources of data).

What Vue 2 feature are Composables designed to replace in Vue 3?

  • Directives
  • Mixins
  • Filters

The Vue community has created massive libraries of pre-built composables, like VueUse, which gives you hundreds of ready-to-use functions.

Level Up 🚀

Advanced cheat sheets, SEO tricks, and interview prep for this topic.

Browser Support

ChromeSupported

Fully supported.

FirefoxSupported

Fully supported.

SafariSupported

Fully supported.

EdgeSupported

Fully supported.

Accessibility (A11y)

1Composables That Manage Focus

A composable like useFocusTrap(containerRef) is the idiomatic Vue way to move keyboard focus into a modal and restore it on close, which keyboard and screen-reader users depend on — accessibility logic like this belongs in a dedicated, reusable composable rather than duplicated onMounted blocks in every dialog component.

SEO Implications

  • 1

    Composables Run Client-Side Only

    Composables that call onMounted, touch window, or fetch data only in the browser never execute during Vue's server-side rendering pass, so any content they produce is invisible in the initial HTML response search engines fetch; content that must be indexable should be fetched through an SSR-aware data layer instead of a composable that only runs after mount.

Best Practices

Return Refs, Not Raw Values

A composable should return reactive refs (const { x, y } = useMouse()) rather than plain destructured numbers, otherwise the component loses reactivity the moment it destructures the returned object.

Accept MaybeRef Arguments

Accept both a plain value and a ref for composable parameters (useFetch(url: MaybeRef<string>)) and unwrap it internally with toValue(), so callers can pass either a static URL or a reactive one that triggers a re-fetch when it changes.

Frequent Bugs

THE BUG

Calling a lifecycle hook like onMounted() inside a composable after an await.

THE FIX

onMounted, onUnmounted, and similar hooks must be called synchronously during the component's setup() call stack. Code like `await fetchConfig(); onMounted(...)` runs onMounted after Vue has lost track of the active component instance, so it silently no-ops — resolve the await inside the component instead, or fire the async work from inside the hook.

Real-World Examples

useLocalStorage Composable

A useLocalStorage(key, defaultValue) composable shared by a settings page and a cart page keeps a ref synced to localStorage.setItem on every change and re-hydrates it on mount, giving both features persistent state without either component knowing localStorage exists.

export function useLocalStorage(key, initial) {
  const data = ref(JSON.parse(localStorage.getItem(key)) ?? initial);
  watch(data, (v) => localStorage.setItem(key, JSON.stringify(v)), { deep: true });
  return data;
}

Interview Prep

?Frequently Asked Questions

Pascual Vila

Pascual Vila

Frontend Instructor // Code Syllabus

Common Pitfalls & Errors

The Error //

Mutating a Prop Directly

// Wrong const props = defineProps(['count']); function increment() { props.count++; } // Correct const emit = defineEmits(['update:count']); function increment() { emit('update:count', props.count + 1); }

The Solution //

A child component should never assign to a prop it received (e.g. props.title = 'New'). Vue logs "Unexpected mutation of prop" because props are a one-way binding. Copy the prop into local state with ref()/computed() first, or emit an event asking the parent to change its own data.

The Error //

Losing Reactivity by Destructuring reactive()

// Wrong const { name } = reactive({ name: 'Alice' }); // name is now a static string // Correct const state = reactive({ name: 'Alice' }); const { name } = toRefs(state); // name is a live ref

The Solution //

Destructuring a reactive() object copies its properties out as disconnected plain values, so future mutations of the original object won't update the destructured variables. Keep referencing the object's properties directly, or convert it with toRefs() before destructuring.

Continue Learning