UI & CLI
Copy React table components into your app with better-tables init.
@better-tables/ui is private — it is not installed from npm. The CLI copies shadcn-style source into your project so you can theme and extend it.
Prerequisites
init refuses to run without a working shadcn setup (components.json in your project) — the copied components build on shadcn primitives and your Tailwind tokens. During init the CLI:
- Verifies shadcn is configured (exits with remediation steps if not)
- Installs the shadcn primitives the table needs (badge, button, dialog, …) via the shadcn CLI
- Checks that
@better-tables/core,@better-tables/adapters-drizzle, and the UI runtime deps (zustand,@dnd-kit/core,@dnd-kit/sortable,@dnd-kit/modifiers,@dnd-kit/utilities) are installed, offering to add them - Copies the Better Tables source into your project
Init
bunx @better-tables/cli init
# or
npx @better-tables/cli init| Flag | Effect |
|---|---|
--components-path <path> | Output directory relative to your shadcn components directory (default better-tables-ui) |
-y, --yes | Skip confirmation prompts (also skips per-file overwrite prompts) |
--skip-shadcn | Skip the shadcn component check/install |
--cwd <path> | Run against a different project directory |
What lands in your project (default paths, resolved through your components.json aliases):
components/better-tables-ui/table/— table, virtualized table, pagination, column visibility/ordering, editable cells, statescomponents/better-tables-ui/filters/— filter bar, dropdown, active filters, faceted sidebar, per-type inputshooks/—use-table-data,use-table-url-sync,use-facets,use-editable-cells, virtualization hooks, …lib/— small shared utilities
Import from the copied paths — there is no @better-tables/ui package to import in your app:
import { BetterTable } from '@/components/better-tables-ui/table/table';Modules
The copied UI ships in modules — named groups of source files you opt into. init copies the core module (the table, filters, hooks, and utilities). Everything else is added on demand:
bunx @better-tables/cli add actions # bulk-action toolbar over selected rowsinit also accepts --modules to opt in at init time:
bunx @better-tables/cli init --modules actions| Module | What it copies | Wire-up |
|---|---|---|
core | Table, filters, hooks, lib (always copied by init) | — |
actions | actions-toolbar.tsx, action-confirmation-dialog.tsx | slots={{ actionsToolbar: ActionsToolbar }} — see Selection & actions |
export | export-button.tsx, use-table-export.ts | slots={{ toolbarExtra: ExportButton }} — see Export |
Modules plug into the table through the slots prop — an absent module is a clean no-op, so core has no dead imports. add <module> reuses your init-time config; pass the same --components-path if you changed it. An unknown module name lists the valid ones and exits non-zero.
Modules vs. plugins. These are different tiers. Modules are copied UI source added with better-tables add (this page). Plugins are npm-distributed data hooks passed to betterTables({ plugins: [...] }) — no UI. Keep the words distinct.
Why copy instead of npm?
- You own the JSX — no fighting a black-box design system
- Works with your existing shadcn / Tailwind tokens
- Avoids bundling conflicts with app-local UI primitives
Docs command
bunx @better-tables/cli docs # this site
bunx @better-tables/cli docs core # /docs/packages/core
bunx @better-tables/cli docs drizzle # /docs/adapters/drizzle
bunx @better-tables/cli docs ui # /docs/packages/uiURL sync hook
After init, use useTableUrlSync from your copied hooks directory — see URL State.