Skip to main content
Web DevelopmentTutorials

The Complete Guide to Vue 3 Composables

Understand the power of Vue 3's Composition API through practical, real-world composable patterns that you can use today.

Mbeah Essilfie

Mbeah Essilfie

July 19, 2026 at 06:02 AM

12 min read 987 views
The Complete Guide to Vue 3 Composables

What Are Composables?

Composables are functions that leverage Vue's Composition API to encapsulate and reuse stateful logic. Think of them as React hooks, but with Vue's reactivity system.

Your First Composable

// composables/useDarkMode.ts
export function useDarkMode() {
  const isDark = ref(false)

  function toggle() {
    isDark.value = !isDark.value
    document.documentElement.classList.toggle('dark', isDark.value)
  }

  onMounted(() => {
    isDark.value = document.documentElement.classList.contains('dark')
  })

  return { isDark, toggle }
}

Real-World Pattern: Data Fetching

// composables/useApi.ts
export function useApi<T>(url: MaybeRef<string>) {
  const data = ref<T | null>(null)
  const error = ref<Error | null>(null)
  const loading = ref(false)

  async function execute() {
    loading.value = true
    error.value = null
    try {
      const response = await fetch(unref(url))
      if (!response.ok) throw new Error(response.statusText)
      data.value = await response.json()
    } catch (e) {
      error.value = e as Error
    } finally {
      loading.value = false
    }
  }

  // Auto-fetch when URL changes
  watch(() => unref(url), execute, { immediate: true })

  return { data, error, loading, refresh: execute }
}
The key insight: composables encapsulate reactive state + logic together. They're not just utility functions — they maintain state across component lifecycles.

Advanced: Shared State Composable

// composables/useAuth.ts
const currentUser = ref<User | null>(null)
const isAuthenticated = computed(() => !!currentUser.value)

export function useAuth() {
  async function login(credentials: LoginInput) {
    const user = await $fetch('/api/auth/login', {
      method: 'POST',
      body: credentials,
    })
    currentUser.value = user
  }

  function logout() {
    currentUser.value = null
    navigateTo('/login')
  }

  return {
    currentUser: readonly(currentUser),
    isAuthenticated,
    login,
    logout,
  }
}

Composable Best Practices

  1. Prefix with use — Convention makes composables instantly recognizable
  2. Return refs, not raw values — Maintains reactivity when destructured
  3. Accept MaybeRef params — Makes composables flexible
  4. Clean up side effects — Use onUnmounted for intervals, listeners, etc.
  5. Keep them focused — One composable, one responsibility

Testing Composables

import { describe, it, expect } from 'vitest'
import { useDarkMode } from './useDarkMode'

describe('useDarkMode', () => {
  it('toggles dark mode', () => {
    const { isDark, toggle } = useDarkMode()
    expect(isDark.value).toBe(false)
    toggle()
    expect(isDark.value).toBe(true)
  })
})

Composables are the backbone of modern Vue development. Master them, and you'll write cleaner, more testable, more reusable code.

JavaScriptTypeScriptVue
Mbeah Essilfie

Written by Mbeah Essilfie

Fullstack Software Developer

Read next