The application UI kit for React and Vue.
Seventy-three components and layouts you copy into your project, built on Tailwind CSS v4. Same API on both frameworks.
npm install @headlessui/react motion clsx @heroicons/reactpnpm add @headlessui/react motion clsx @heroicons/reactyarn add @headlessui/react motion clsx @heroicons/reactbun add @headlessui/react motion clsx @heroicons/reactnpm install reka-ui motion-v @heroicons/vuepnpm add reka-ui motion-v @heroicons/vueyarn add reka-ui motion-v @heroicons/vuebun add reka-ui motion-v @heroicons/vueStats and meters
DocsBars under the figure, a qualified change, an animated meter.
- Revenue
- $12,480
- 14% up on last month (good)
- Sign-ups
- 1,284
- 9% down on last month (bad)
Catalogue
Seventy-three components, twelve categories.
From a page shell to a single status dot. Each category has its own page in the docs, and every component ships with its examples and its API.
12 Rue de la Paix, 75002 Paris, France
Same as billing.
Collapsible section, Detail header, Divider, Heading and 4 more.
pay_3K9dQ
22 Sep
$1,240.00
pay_8Fh2L
21 Sep
$86.00
Data table, Export menu, Filter bar, Table and 1 more.
- Customer
- Daniel Essomba
- Status
- active
- Reference
- cus_8F3K2QW
Avatar, Badge, Description list, Fact list and 2 more.
Payment captured
Mariam added a note
Customer asked for a copy of the invoice.
Invoice sent
Notes, Notification, Timeline.
Saving changes…
Alert, Empty page, Empty state, Notice and 3 more.
73
Components and layouts in 12 categories, with the same API in React and Vue. Copied into your project, yours to change.
Browse allPrinciples
Built the way you would build it yourself.
Four things that are true today, nothing that is still on a roadmap.
Yours, in your repo.
Every component is a file you copy into your project. Rename it, restyle it, delete what you do not need. Nothing to update, nothing to wait on.
src/components/button.tsx
import * as Headless from '@headlessui/react'
import clsx from 'clsx'
import React, { forwardRef } from 'react'
import { Link } from './link'
const styles = {
base: [
// Base
'relative isolate inline-flex items-baseline justify-center gap-x-2 rounded-lg border text-base/6 font-semibold',
// Sizing
'px-[calc(--spacing(3.5)-1px)] py-[calc(--spacing(2.5)-1px)] sm:px-[calc(--spacing(3)-1px)] sm:py-[calc(--spacing(1.5)-1px)] sm:text-sm/6',src/components/button.vue
<script setup lang="ts">
import { computed } from 'vue'
import { vInteractions } from './interactions'
import Link from './link.vue'
import TouchTarget from './touch-target.vue'
const styles = {
base: [
// Base
'relative isolate inline-flex items-baseline justify-center gap-x-2 rounded-lg border text-base/6 font-semibold',
// Sizing
'px-[calc(--spacing(3.5)-1px)] py-[calc(--spacing(2.5)-1px)] sm:px-[calc(--spacing(3)-1px)] sm:py-[calc(--spacing(1.5)-1px)] sm:text-sm/6',One API, two frameworks.
The React kit builds on Headless UI, the Vue kit on Reka UI. Same names, same props, same slots, so the docs read the same whichever you pick.
import { Button } from '@/components/button'
export default function Page() {
return (
<Button color="light" href="/settings">
Settings
</Button>
)
}<script setup lang="ts">
import Button from '@/components/button.vue'
</script>
<template>
<Button color="light" href="/settings">
Settings
</Button>
</template>Every state, in the file.
Hover, focus, active, open, disabled: each one is a data attribute, styled with plain Tailwind classes you can read and change.
button.tsx
const styles = {
base: [
// Focus
'focus:not-data-focus:outline-hidden data-focus:outline-2 data-focus:outline-offset-2 data-focus:outline-accent-500',
// Disabled
'data-disabled:opacity-50',
],
plain: [
// Hover and active
'border-transparent text-zinc-950 data-active:bg-zinc-950/5 data-hover:bg-zinc-950/5',
// Dark mode
'dark:text-white dark:data-active:bg-white/10 dark:data-hover:bg-white/10',
],
}button.vue
const styles = {
base: [
// Focus
'focus:not-data-focus:outline-hidden data-focus:outline-2 data-focus:outline-offset-2 data-focus:outline-accent-500',
// Disabled
'data-disabled:opacity-50',
],
plain: [
// Hover and active
'border-transparent text-zinc-950 data-active:bg-zinc-950/5 data-hover:bg-zinc-950/5',
// Dark mode
'dark:text-white dark:data-active:bg-white/10 dark:data-hover:bg-white/10',
],
}Tokens, not a theme file.
Tailwind CSS v4 tokens, with an accent you set once as CSS variables. Dark mode is a variant, not a second stylesheet.
app.css
@import "tailwindcss";
@theme inline {
--color-accent-500: var(--accent-500, var(--color-blue-500));
--color-accent-600: var(--accent-600, var(--color-blue-600));
}
:root {
--accent-500: oklch(0.65 0.118 184.7);
--accent-600: oklch(0.6 0.118 184.7);
}Theme
Your brand color, in seven lines.
Pick a hue. The kit derives the seven stops it reads, and every component follows, in light and in dark. Paste the block in your CSS and you are done.
Accent hue
184.7°
| Sun | Mon | Tue | Wed | Thu | Fri | Sat |
|---|---|---|---|---|---|---|
app.css
:root {
--accent-200: oklch(0.882 0.033 184.7);
--accent-300: oklch(0.809 0.058 184.7);
--accent-400: oklch(0.707 0.091 184.7);
--accent-500: oklch(0.65 0.118 184.7);
--accent-600: oklch(0.6 0.118 184.7);
--accent-700: oklch(0.53 0.125 184.7);
--accent-900: oklch(0.379 0.081 184.7);
}