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 | 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): Controlled State, Inject Context, Virtualization, Migration
Usage Pattern
Load based on context:
- Component index → references/components.md
- Specific component → components/dialog.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
ts1// nuxt.config.ts (auto-imports all components) 2export default defineNuxtConfig({ 3 modules: ['reka-ui/nuxt'] 4})
ts1import { RekaResolver } from 'reka-ui/resolver' 2// vite.config.ts (with auto-import resolver) 3import Components from 'unplugin-vue-components/vite' 4 5export default defineConfig({ 6 plugins: [vue(), Components({ resolvers: [RekaResolver()] })] 7})
Basic Patterns
vue1<!-- Dialog with controlled state --> 2<script setup> 3 import { 4 DialogRoot, 5 DialogTrigger, 6 DialogPortal, 7 DialogOverlay, 8 DialogContent, 9 DialogTitle, 10 DialogDescription, 11 DialogClose 12 } from 'reka-ui' 13 const open = ref(false) 14</script> 15 16<template> 17 <DialogRoot v-model:open="open"> 18 <DialogTrigger>Open</DialogTrigger> 19 <DialogPortal> 20 <DialogOverlay class="fixed inset-0 bg-black/50" /> 21 <DialogContent 22 class="fixed left-1/2 top-1/2 -translate-x-1/2 -translate-y-1/2 bg-white p-6 rounded" 23 > 24 <DialogTitle>Title</DialogTitle> 25 <DialogDescription>Description</DialogDescription> 26 <DialogClose>Close</DialogClose> 27 </DialogContent> 28 </DialogPortal> 29 </DialogRoot> 30</template>
vue1<!-- Select with uncontrolled default --> 2<SelectRoot default-value="apple"> 3 <SelectTrigger> 4 <SelectValue placeholder="Pick fruit" /> 5 </SelectTrigger> 6 <SelectPortal> 7 <SelectContent> 8 <SelectViewport> 9 <SelectItem value="apple"><SelectItemText>Apple</SelectItemText></SelectItem> 10 <SelectItem value="banana"><SelectItemText>Banana</SelectItemText></SelectItem> 11 </SelectViewport> 12 </SelectContent> 13 </SelectPortal> 14</SelectRoot>
vue1<!-- asChild for custom trigger element --> 2<DialogTrigger as-child> 3 <button class="my-custom-button">Open</button> 4</DialogTrigger>
Recent Updates (v2.5.0-v2.7.0)
- New composables exposed:
useLocale,useDirection(v2.6.0) - Select: Added
disableOutsidePointerEventsprop to Content - Toast: Added
disableSwipeprop for swipe control - DatePicker: Added
closeOnSelectproperty - ContextMenu: Added
pressOpenDelayfor long-press configuration - Virtualization:
estimateSizenow accepts function for Listbox/Tree (v2.7.0); supported in Combobox, Listbox, Tree
Resources
- Reka UI Docs
- GitHub
- Nuxt UI (styled Reka components)
- shadcn-vue (styled Reka components)
Token efficiency: ~350 tokens base, components.md index ~100 tokens, per-component ~50-150 tokens