A composable React framework for data-driven applications. Two UI editions — Material UI and Tailwind — share a single type-safe form bridge, an RBAC engine, and a reactive theming system. Write your form schema and access rules once, render them with either visual stack.
📚 Documentation · 🚀 Starter Kits · 📝 Changelog · 🔄 Migration
Building admin panels, dashboards and internal tools in React typically
means writing the same <Controller> wrapper, the same RBAC
hide/disable/readonly logic, the same dynamic-field engine, and the
same theme plumbing — in every project. Dashforge is the layer that
codifies those patterns, so you stop rewriting them.
- Form bridge over
react-hook-form—DashForm+ per-field subscriptions. Fewer re-renders than plain RHF, zero<Controller>boilerplate at call sites. - RBAC built in — every field accepts an
accessprop withhide/disable/readonlysemantics evaluated against the current subject + policy. - Reactions & dependent fields — declarative
watch→runrules with async-safe stale-response guards. - Reactive theming — Valtio-powered store, mode swap (light/dark), cross-tab sync. No CSS rebuild on theme change.
- Two skins, one contract — pick MUI v9 or Tailwind; the bridge layer is identical on both. Swap (or run both) without rewriting business logic.
| You want… | Use | Why |
|---|---|---|
| MUI v9 + Emotion as the design system | @dashforge/ui |
Thin layer over @mui/material; for Table / DataGrid / Dialog / Tooltip / Popover use MUI X and @mui/material directly |
| Tailwind CSS as the design system | @dashforge/tw |
Self-contained component library (37 components incl. Table, DataGrid, Pagination, Dialog, Tooltip, Popover, Accordion, Foundation primitives) — no MUI dependency |
| Just the form bridge | @dashforge/forms + @dashforge/rbac |
Visual-stack agnostic; bring your own components and use the bridge hooks |
The two editions are fully isolated at the visual layer. They
share only the bridge (@dashforge/forms, @dashforge/rbac, theming
core, @dashforge/calendar-core). A form schema written for MUI runs
unchanged on Tailwind and vice versa.
npm install @dashforge/ui @dashforge/forms @dashforge/rbac \
@dashforge/theme-mui \
@mui/material @emotion/react @emotion/styled react-hook-formThe bridge core (@dashforge/tokens, @dashforge/theme-core,
@dashforge/ui-core, @dashforge/calendar-core) is pulled in
transitively.
import { DashForm } from '@dashforge/forms';
import { TextField, Select, Button } from '@dashforge/ui';
export function SignupForm() {
return (
<DashForm
defaultValues={{ email: '', plan: 'free' }}
onSubmit={(data) => console.log(data)}
>
<TextField
name="email"
label="Email"
rules={{ required: 'Email is required' }}
/>
<Select
name="plan"
label="Plan"
options={[
{ value: 'free', label: 'Free' },
{ value: 'pro', label: 'Pro' },
]}
/>
<Button type="submit" variant="contained">Sign up</Button>
</DashForm>
);
}npm install @dashforge/tw @dashforge/tw-theme \
@dashforge/forms @dashforge/rbac tailwindcss@dashforge/tw-tokens and @dashforge/calendar-core are pulled in
transitively.
// tailwind.config.ts
import { dashforgePreset } from '@dashforge/tw-theme';
export default {
presets: [dashforgePreset()],
content: ['./src/**/*.{ts,tsx}'],
};import { DashforgeTailwindProvider } from '@dashforge/tw-theme';
import { DashForm } from '@dashforge/forms';
import { TextField, Select, Button } from '@dashforge/tw';
export function App() {
return (
<DashforgeTailwindProvider>
<DashForm
defaultValues={{ email: '', plan: 'free' }}
onSubmit={(data) => console.log(data)}
>
<TextField
name="email"
label="Email"
rules={{ required: 'Email is required' }}
/>
<Select
name="plan"
label="Plan"
options={[
{ value: 'free', label: 'Free' },
{ value: 'pro', label: 'Pro' },
]}
/>
<Button type="submit" variant="solid">Sign up</Button>
</DashForm>
</DashforgeTailwindProvider>
);
}The form schema (name, rules, RBAC access, visibleWhen,
reactions) is byte-identical across both editions.
┌─────────────────────────────────────────────┐
│ Your application │
└─────────────────────────────────────────────┘
│ │
▼ ▼
┌─────────────────┐ ┌─────────────────┐
│ @dashforge/ui │ │ @dashforge/tw │
│ (MUI skin) │ │ (Tailwind skin) │
└────────┬────────┘ └────────┬────────┘
│ │
└──────────────┬──────────────┘
│
▼
┌──────────────────────────────────────────┐
│ Shared bridge layer │
│ forms · rbac · ui-core · calendar-core │
│ tokens · theme-core · theme-mui · │
│ tw-tokens · tw-theme │
└──────────────────────────────────────────┘
The bridge layer is the portability contract. Components on either
edition consume it through the same useDashFieldMeta /
useDashRegister / useRbac hooks — guaranteeing identical behavior
across visual stacks.
Eleven publishable packages, two visual editions, one shared core.
| Package | Description |
|---|---|
@dashforge/ui |
Component library on MUI v9 — TextField, Select, Autocomplete, Calendar, Tabs, AppShell, and more |
@dashforge/theme-mui |
MUI theme adapter — bridges Dashforge tokens to MUI's theming system |
| Package | Description |
|---|---|
@dashforge/tw |
Component library on Tailwind — 37 components including Table, DataGrid, Pagination, Dialog, Tooltip, Popover, Accordion + Foundation primitives |
@dashforge/tw-theme |
Tailwind preset + reactive provider — dashforgePreset(), CSS-var runtime, mode switching |
@dashforge/tw-tokens |
Design tokens for the Tailwind edition |
| Package | Description |
|---|---|
@dashforge/forms |
Type-safe form bridge on react-hook-form — DashForm, per-field subscriptions, reactions, field arrays |
@dashforge/rbac |
Role-based access control engine — policies, subjects, <Can>, useRbac, per-field access prop |
@dashforge/ui-core |
Headless utilities — engine, dependency tracker, rule evaluator, store helpers |
@dashforge/calendar-core |
Headless calendar / date-range engine shared by both editions' date pickers |
@dashforge/theme-core |
Reactive theming store (Valtio) — mode swap, cross-tab sync |
@dashforge/tokens |
Design tokens for the MUI edition |
DashFormwith per-field subscriptions (fewer renders than plain RHF)- Async-safe reactions with stale-response guards (
isLatest()) - Runtime field data — populate
Select/Autocompleteoptions from reaction output without bridge ceremony useDashFieldArrayfor dynamic lists with stable keys- Form Closure v1 error gating (errors show on
touched || submitCount > 0) - Schema-agnostic: works with any resolver (
zod,yup,joi, custom)
- Declarative policies with
Subject,Resource,Action,Effect - Per-field
accessprop withhide/disable/readonlysemantics - Group + option-level access on
RadioGroup <Can>anduseCan()for arbitrary gated regions- Pluggable condition evaluators
- MUI (
@dashforge/ui): 23 components — text fields, selects, autocompletes, switches, radio groups, calendar, date / time / range pickers, OTP field, tabs, AppShell, top bar, breadcrumbs, snackbar, confirm dialog, left nav, more - Tailwind (
@dashforge/tw): 37 components — everything above plus Table, DataGrid, Pagination, Skeleton, Dialog, Tooltip, Popover, Accordion, and Foundation primitives (Box, Stack, Grid, Container, Divider, AspectRatio, Typography, VisuallyHidden)
- Token-driven design system — colors, typography, spacing, radii
- CSS-var runtime — theme changes do not retrigger Tailwind builds
- Light / dark mode swap + cross-tab synchronisation
- Per-tenant theming via single-token overrides
- Live docs site: https://dashforge-ui.com — pages for every component with inline live previews and "Open in StackBlitz" sandboxes
- Starter kits: https://dashforge-ui.com/starter-kits
- Per-package READMEs:
libs/dashforge/<pkg>/README.md - Per-package CHANGELOGs:
libs/dashforge/<pkg>/CHANGELOG.md - Migration guide:
MIGRATION.md
- Node ≥ 22
- React 18 or 19
- MUI edition:
@mui/material@^9.0.0+@emotion/react+@emotion/styled - Tailwind edition:
tailwindcss ≥ 3.4.1
Dashforge is at 1.0.0 — production-ready, public API stable
under semver. Breaking changes require a major bump.
Each package versions independently post-1.0.0. Cross-package
compatibility is governed by the peer-dependency ranges declared in
each package.json.
The codebase is an Nx monorepo using pnpm workspaces. Common workflows:
pnpm install # install workspace deps
pnpm exec nx run-many -t lint typecheck test build # the full gate
pnpm exec nx run @dashforge/<pkg>:test # single package testsContributions are welcome — please align with the existing architecture, type-safety conventions, and the testing patterns documented in each package.
MIT — © 2026 Dashforge contributors.