Building Headless Components in Vue — The Right Way

Youssef Abdulaziz

Youssef Abdulaziz

2025-05-30T17:04:20Z

1 min read

Reusable UI components often break once you change the design. Solution? Go headless.

A headless component:

  • "Exposes data & logic (but no styling)"

  • "Lets you plug in any markup/UX you want"

  • "Keeps behavior testable and DRY"

Example: a headless dropdown:

const useDropdown = () => {
  const isOpen = ref(false)
  const toggle = () => isOpen.value = !isOpen.value
  return { isOpen, toggle }
}
Enter fullscreen mode Exit fullscreen mode
<template>
  <button @click="toggle">Toggle</button>
  <div v-if="isOpen"><slot /></div>
</template>
Enter fullscreen mode Exit fullscreen mode

Benefits:

  • "📦 Easy to theme"

  • "✅ Easier snapshot testing"

  • "🧠 Decouples logic from layout"

This is what libraries like Radix and Headless UI follow. You can build your own system for any internal tools.