A professional Vue 3 toolkit — the components a product interface is built from. Built on Reka UI.
Documentation · Storybook · Roadmap
A product interface is a set of components that have to agree: controls, overlays, tables, filters, empty and loading states. rowkit is that set, built as one toolkit — typed, on Reka UI, from one token package. What is published today is the part already finished. The rest of the set is the plan in the roadmap, not a second library to go and find.
npm i rowkitThen, in your stylesheet — both lines, in this order:
@import 'tailwindcss';
@import 'rowkit/styles';The second line is the step people miss. Tailwind does not scan node_modules, so without it every component renders unstyled, with nothing in the console. vue and tailwindcss are peer dependencies.
<script setup lang="ts">
import { DataTable, Badge, useClientSort, type DataTableColumn } from 'rowkit'
import { ref } from 'vue'
interface User {
id: number
name: string
role: string
status: 'active' | 'invited' | 'suspended'
}
// `key` is constrained to keyof User — a renamed field is a compile error,
// not a column of blanks.
const columns: DataTableColumn<User>[] = [
{ key: 'name', header: 'Name', sortable: true, sticky: true },
{ key: 'role', header: 'Role', sortable: true },
{ key: 'status', header: 'Status' },
]
const users = ref<User[]>([
{ id: 1, name: 'Ada Lovelace', role: 'Owner', status: 'active' },
{ id: 2, name: 'Grace Hopper', role: 'Admin', status: 'invited' },
])
const sort = ref()
const rows = useClientSort(users, sort, columns)
</script>
<template>
<DataTable :rows="rows" :columns="columns" caption="Team members" v-model:sort="sort" hoverable>
<template #[`cell:status`]="{ value }">
<Badge :variant="value === 'active' ? 'success' : 'warning'" dot>{{ value }}</Badge>
</template>
</DataTable>
</template>The table reports the sort and renders what it is handed — it never reorders its own rows, which is what keeps a server-paged table honest. useClientSort does the local case.
- Columns typed against your row.
DataTable<TRow>constrains every column'skeytokeyof TRow. Sorting names a field too, so a sort referring to a column that does not exist also fails to compile. - Accessibility as a build gate. Focus traps, scroll lock, live regions and keyboard models come from Reka UI primitives. Every Storybook story is scanned by axe as part of the test run — a violation fails CI rather than filling a panel nobody opens.
- Three states that agree.
Skeleton,EmptyStateand the no-results case are designed together, because the bug is never one of them alone. - Tokens all the way down. Every colour, space, radius and layer lives in
@rowkit/tokens, installable on its own. Contrast pairings are asserted in tests, not eyeballed. - State you own. Sort, selection, page and filters are all
v-model. Components report what happened; your application decides what follows.
The published components are the start of a professional toolkit, not a specialist for tables. The full set — menus, dates, overlays, and the rest a product interface needs — is planned in the roadmap. It is not being built ahead of that plan.
AGENTS.md ships inside the package. After installing, node_modules/rowkit/AGENTS.md describes every component's props, v-models, events and slots — generated from the source, so it describes the version you installed.
v0.x. The API is stabilising toward v1.0 and breaking changes are still possible until then. Releases are cut from CI with provenance attestation, and the changelog is changesets-driven: rowkit · @rowkit/tokens.
See docs/contributing for setup, the definition of done, and the changeset requirement.
pnpm install
pnpm build # run first — workspace packages resolve through dist
pnpm dev # playground app
pnpm storybook # component workshop
pnpm test # unit, component and browser tests
pnpm docs:dev # documentation site
pnpm docs:shots # refresh README homepage screenshots (docs:dev must be running)MIT © Nikolai Kushner
Design language based on shadcn/ui by shadcn, adapted for Vue. shadcn/ui is MIT licensed; rowkit adopts its token values and class recipes, not its code.

