---
title: Components Guide - Headless Vue 3 UI Primitives
features:
  order: 2
  level: 2
meta:
  - name: description
    content: Build accessible, customizable Vue 3 components with Vuetify0. Learn the compound component pattern, context injection, and v-model integration for headless UI.
  - name: keywords
    content: vuetify0, components, headless ui, compound pattern, accessibility, v-model, Vue 3, unstyled
related:
  - /guide/fundamentals/core
  - /guide/fundamentals/composables
  - /guide/features/accessibility
---

# Components

v0 components are Vue wrappers around composables. Composables hold logic, components provide Vue integration via slots, props, and emits.

<DocsPageFeatures :frontmatter />

## Component Philosophy

- **Headless**: Zero styling - you own all CSS
- **Slot-driven**: All customization through scoped slots
- **Accessible**: ARIA attributes via `attrs` object
- **Composable-backed**: Use components or composables directly

> [!TIP]
> Always spread the `attrs` object onto your elements. It contains ARIA attributes and data attributes for accessibility and styling.

## Component Categories

| Category | Purpose | Examples |
| - | - | - |
| **Primitives** | Base building blocks | [Atom](/components/primitives/atom), [Presence](/components/primitives/presence) — [all primitives](/components#primitives) |
| **Providers** | Pure state management, no DOM | [Selection](/components/providers/selection), [Theme](/components/providers/theme) — [all providers](/components#providers) |
| **Actions** | Interactive controls that trigger behavior | [Button](/components/actions/button), [Toggle](/components/actions/toggle) — [all actions](/components#actions) |
| **Forms** | Form controls with accessibility | [Checkbox](/components/forms/checkbox), [Select](/components/forms/select), [Form](/components/forms/form) — [all forms](/components#forms) |
| **Semantic** | Meaningful HTML defaults | [Avatar](/components/semantic/avatar), [Pagination](/components/semantic/pagination) — [all semantic](/components#semantic) |
| **Disclosure** | Show/hide patterns | [Dialog](/components/disclosure/dialog), [AlertDialog](/components/disclosure/alert-dialog), [Tabs](/components/disclosure/tabs) — [all disclosure](/components#disclosure) |

Full inventory with descriptions: [Components](/components).

## Atom: The Foundation

The `Atom` component is a polymorphic base element supporting any HTML tag:

```vue
<script setup lang="ts">
  function onClick() {
    console.log('clicked')
  }
</script>

<template>
  <!-- Render as button -->
  <Atom as="button" @click="onClick">Click me</Atom>

  <!-- Render as link -->
  <Atom as="a" href="/path">Navigate</Atom>

  <!-- Renderless mode - slot only -->
  <Atom :as="null" v-slot="{ attrs }">
    <MyCustomComponent v-bind="attrs" />
  </Atom>
</template>
```

### Rendering Modes

| Mode | Usage | Output |
| - | - | - |
| Element | `as="button"` | `<button>` with slot content |
| Renderless | `:as="null"` or `renderless` | Slot only, no wrapper |

## Slot Props Pattern

Every component exposes `attrs` in its default slot. Spread onto your element for behavior and accessibility — `attrs` already includes the click handler plus ARIA and `data-*` attributes. Do not also bind `@click` when spreading `attrs`, or both handlers fire.

```vue playground
<script setup lang="ts">
  import { Selection } from '@vuetify/v0'

  const items = ['Apple', 'Banana', 'Cherry']
</script>

<template>
  <Selection.Root>
    <Selection.Item v-for="item in items" v-slot="{ attrs, isSelected }">
      <button
        v-bind="attrs"
        :class="{ 'bg-primary': isSelected }"
      >
        {{ item }}
      </button>
    </Selection.Item>
  </Selection.Root>
</template>
```

### Common Slot Props

| Component Type | Slot Props |
| - | - |
| Selection.Item | `attrs`, `isSelected`, `toggle`, `select`, `unselect` |
| Group.Item | `attrs`, `isSelected`, `isMixed`, `toggle` |
| ExpansionPanel.Activator | `attrs`, `isSelected`, `toggle` |
| Popover.Root | `id`, `isSelected`, `toggle` |
| Popover.Activator | `attrs`, `isOpen` |
| Popover.Content | `attrs`, `isOpen` |

### Data Attributes

Components emit data attributes for CSS styling:

```css
[data-selected] { background: var(--primary); }
[data-disabled] { opacity: 0.5; }
[data-mixed] { /* tri-state checkbox */ }
[data-open] { /* popover is visible */ }
```

> [!ASKAI] How do I add styling or CSS classes to headless components?
