home / skills / onmax / nuxt-skills / reka-ui

reka-ui skill

/skills/reka-ui

This skill helps you build accessible, headless Vue components with Reka UI by guiding patterns, state management, virtualization, and styling integration.

npx playbooks add skill onmax/nuxt-skills --skill reka-ui

Review the files below or copy the command above to add this skill to your agents.

Files (53)
SKILL.md
4.9 KB
---
name: reka-ui
description: Use when building with Reka UI (headless Vue components) - provides component API, accessibility patterns, composition (asChild), controlled/uncontrolled state, virtualization, and styling integration. Formerly Radix Vue.
license: MIT
---

# Reka UI

Unstyled, accessible Vue 3 component primitives. WAI-ARIA compliant. Previously Radix Vue.

**Current version:** v2.7.0 (December 2025)

## When to Use

- Building headless/unstyled components from scratch
- Need WAI-ARIA compliant components
- Using Nuxt UI, shadcn-vue, or other Reka-based libraries
- Implementing accessible forms, dialogs, menus, popovers

**For Vue patterns:** use `vue` skill

## Available Guidance

| File                                                     | Topics                                                              |
| -------------------------------------------------------- | ------------------------------------------------------------------- |
| **[references/components.md](references/components.md)** | Component index by category (Form, Date, Overlay, Menu, Data, etc.) |
| **components/\*.md**                                     | Per-component details (dialog.md, select.md, etc.)                  |

**New guides** (see [reka-ui.com](https://reka-ui.com)): Controlled State, Inject Context, Virtualization, Migration

## Usage Pattern

**Load based on context:**

- Component index → [references/components.md](references/components.md)
- Specific component → [components/dialog.md](components/dialog.md), [components/select.md](components/select.md), etc.
- For styled Nuxt components built on Reka UI → use **nuxt-ui** skill

## Key Concepts

| Concept                 | Description                                                           |
| ----------------------- | --------------------------------------------------------------------- |
| `asChild`               | Render as child element instead of wrapper, merging props/behavior    |
| Controlled/Uncontrolled | Use `v-model` for controlled, `default*` props for uncontrolled       |
| Parts                   | Components split into Root, Trigger, Content, Portal, etc.            |
| `forceMount`            | Keep element in DOM for animation libraries                           |
| Virtualization          | Optimize large lists (Combobox, Listbox, Tree) with virtual scrolling |
| Context Injection       | Access component context from child components                        |

## Installation

```ts
// nuxt.config.ts (auto-imports all components)
export default defineNuxtConfig({
  modules: ['reka-ui/nuxt']
})
```

```ts
import { RekaResolver } from 'reka-ui/resolver'
// vite.config.ts (with auto-import resolver)
import Components from 'unplugin-vue-components/vite'

export default defineConfig({
  plugins: [
    vue(),
    Components({ resolvers: [RekaResolver()] })
  ]
})
```

## Basic Patterns

```vue
<!-- Dialog with controlled state -->
<script setup>
import { DialogRoot, DialogTrigger, DialogPortal, DialogOverlay, DialogContent, DialogTitle, DialogDescription, DialogClose } from 'reka-ui'
const open = ref(false)
</script>

<template>
  <DialogRoot v-model:open="open">
    <DialogTrigger>Open</DialogTrigger>
    <DialogPortal>
      <DialogOverlay class="fixed inset-0 bg-black/50" />
      <DialogContent class="fixed left-1/2 top-1/2 -translate-x-1/2 -translate-y-1/2 bg-white p-6 rounded">
        <DialogTitle>Title</DialogTitle>
        <DialogDescription>Description</DialogDescription>
        <DialogClose>Close</DialogClose>
      </DialogContent>
    </DialogPortal>
  </DialogRoot>
</template>
```

```vue
<!-- Select with uncontrolled default -->
<SelectRoot default-value="apple">
  <SelectTrigger>
    <SelectValue placeholder="Pick fruit" />
  </SelectTrigger>
  <SelectPortal>
    <SelectContent>
      <SelectViewport>
        <SelectItem value="apple"><SelectItemText>Apple</SelectItemText></SelectItem>
        <SelectItem value="banana"><SelectItemText>Banana</SelectItemText></SelectItem>
      </SelectViewport>
    </SelectContent>
  </SelectPortal>
</SelectRoot>
```

```vue
<!-- asChild for custom trigger element -->
<DialogTrigger as-child>
  <button class="my-custom-button">Open</button>
</DialogTrigger>
```

## Recent Updates (v2.5.0-v2.7.0)

- **New composables exposed**: `useLocale`, `useDirection` (v2.6.0)
- **Select**: Added `disableOutsidePointerEvents` prop to Content
- **Toast**: Added `disableSwipe` prop for swipe control
- **DatePicker**: Added `closeOnSelect` property
- **ContextMenu**: Added `pressOpenDelay` for long-press configuration
- **Virtualization**: `estimateSize` now accepts function for Listbox/Tree (v2.7.0); supported in Combobox, Listbox, Tree

## Resources

- [Reka UI Docs](https://reka-ui.com)
- [GitHub](https://github.com/unovue/reka-ui)
- [Nuxt UI](https://ui.nuxt.com) (styled Reka components)
- [shadcn-vue](https://www.shadcn-vue.com) (styled Reka components)

---

_Token efficiency: ~350 tokens base, components.md index ~100 tokens, per-component ~50-150 tokens_

Overview

This skill provides focused guidance for building with Reka UI: a headless, WAI-ARIA compliant set of Vue 3 primitives (formerly Radix Vue). It explains component APIs, accessibility patterns, composition via asChild, controlled vs uncontrolled state, virtualization, and styling integration. Use it to implement accessible, unstyled components or to power styled libraries like Nuxt UI or shadcn-vue.

How this skill works

The skill inspects Reka UI primitives and documentation to surface per-component APIs (Root, Trigger, Content, Portal, parts) and recommended patterns such as v-model controlled state versus default* uncontrolled props. It highlights composition helpers (asChild), context injection, forceMount usage, and virtualization options for large lists. Installation and auto-import patterns for Nuxt and Vite are included to speed integration into Vue/Nuxt projects.

When to use it

  • Building headless or unstyled Vue 3 components that must be accessible
  • Implementing dialogs, menus, selects, popovers, lists, or date pickers with WAI-ARIA patterns
  • Integrating Reka-based styled libraries (Nuxt UI, shadcn-vue) or migrating from Radix Vue
  • Optimizing large lists (Combobox, Listbox, Tree) with virtualization
  • Needing fine-grained control over mounting, composition, and context injection

Best practices

  • Prefer v-model for controlled components and default* props for simple uncontrolled usage
  • Use asChild to pass behavior to custom trigger elements while retaining accessibility
  • Split UI into Parts (Root, Trigger, Content, Portal) to keep markup predictable and testable
  • Enable forceMount during animations to avoid mount/unmount layout shifts
  • Use virtualization (estimateSize, function support) for large list performance and combine with keyboard navigation patterns

Example use cases

  • Controlled Dialog pattern with DialogRoot v-model:open and separate Portal/Overlay/Content for accessibility and focus management
  • Uncontrolled Select using default-value and SelectViewport with virtualized items for long option lists
  • Custom trigger button via asChild to attach dialog behavior to bespoke UI without extra wrappers
  • Migrating a Radix Vue codebase to Reka UI while preserving accessibility contracts and updating to new composables (useLocale, useDirection)
  • Adding swipe or press configuration (Toast disableSwipe, ContextMenu pressOpenDelay) to tailor interaction timing

FAQ

How do I choose controlled vs uncontrolled components?

Use v-model when parent needs to manage state; use default* props for simpler local state. Controlled gives predictable external sync; uncontrolled reduces boilerplate.

When should I use forceMount?

Enable forceMount when animations require elements to remain in the DOM to animate in/out reliably, or when measurement must occur before showing content.