@vibeflui/core
v1.1.0
Published
AI-friendly schema-driven UI framework for React and Next.js.
Maintainers
Readme
VibeFlui
Generate Forms, Tables and Actions from JSON.
AI-friendly schema-driven UI framework for React & Next.js.
Generate forms, tables, actions, and admin UIs from JSON — less code, less time, fewer tokens.
Documentation | AI Source Map | npm
VibeFlui is the project. FluiKit is the core UI component.
<FluiKit schema={schema} />FluiKit helps you reduce React boilerplate, build admin UI faster, write less JSX, reduce the number of tokens needed by AI coding assistants, and make vibe coding workflows more reliable. Instead of asking an AI tool to generate hundreds of lines of page-specific JSX, you ask it to generate a compact schema.
Why VibeFlui?
Modern React and Next.js CRUD screens repeat the same patterns again and again.
Traditional React CRUD often requires developers or AI assistants to write:
<Table />
<TableColumn />
<Form />
<FormField />
<Modal />
<Search />
<Pagination />
<DeleteDialog />Then the page still needs state, row mapping, nested value access, validation, loading states, empty states, action handlers, response mapping, and integration glue.
The result is familiar:
- A lot of boilerplate.
- Repeated code across resources.
- More JSX to maintain.
- Harder AI output review.
- More tokens consumed by AI assistants.
- Slower iteration when schemas and backend endpoints change.
The FluiKit Approach
With FluiKit, the screen starts from schema:
<FluiKit schema={userSchema} />Or:
const userSchema = {
resource: "users",
table: {
columns: ["name", "email", "role.name", "status"]
},
form: {
fields: ["name", "email", "roleId"]
},
actions: true
}FluiKit turns the schema into UI automatically.
Your backend still owns business logic, authentication, authorization, database writes, final validation, workflow, audit logs, and security. VibeFlui is responsible for the UI layer.
AI Token Saving
VibeFlui is designed for the AI coding era.
Traditional prompt:
Create a user management page.
Build:
- table
- pagination
- search
- modal
- create form
- edit form
- delete dialogAI assistants usually respond with hundreds or thousands of lines of JSX, state management, table markup, form inputs, modal code, and action handlers.
FluiKit prompt:
Generate FluiKit schema for user management.
Fields:
- name
- email
- role
- statusAI only needs to generate schema:
const schema = {
resource: "users",
endpoint: "/api/users",
table: {
columns: ["name", "email", "role.name", "status"]
},
form: {
fields: ["name", "email", "roleId"]
},
actions: true
}Benefits:
- Fewer tokens.
- Less generated output.
- Faster AI generation.
- More consistent UI.
- Easier code review.
- Easier schema validation.
FluiKit is designed to reduce AI token usage by replacing repetitive JSX generation with compact schema definitions.
Why It Matters
For vibe coders and AI-assisted teams, schema-driven UI means:
- Less prompting.
- Less token usage.
- Faster iterations.
- More consistent output.
- Easier AI generation.
- Smaller diffs.
- Fewer repeated UI bugs.
Features
| Feature | Supported | | --- | --- | | Form Generator | ✅ | | Table Generator | ✅ | | CRUD Actions | ✅ | | Search | ✅ | | Filter | ✅ | | Pagination | ✅ | | Nested Mapping | ✅ | | Edit Mapping | ✅ | | Identity Keys | ✅ | | Custom Actions | ✅ | | Detail View | ✅ | | Readonly Mode | ✅ | | Layout Tabs | ✅ | | Wizard Forms | ✅ | | Form Sections | ✅ | | Multi-resource Pages | ✅ | | Registry Pattern | ✅ | | JSON Schema | ✅ | | Zod Validation | ✅ | | Tailwind | ✅ | | TailAdmin | ✅ | | TypeScript | ✅ | | AI-Friendly | ✅ | | Token Saving | ✅ | | Built-in Field Validation | ✅ | | Dashboard Cards | ✅ | | FluiKitCard | ✅ | | FluiKitTab | ✅ | | Styled Confirm Dialog | ✅ | | Plugin System | ✅ | | Optional React Query Package | ✅ | | Optional UI Adapter Packages | ✅ | | OpenAPI Generator | 🚧 Roadmap | | FluiKit Studio | 🚧 Roadmap |
Current Status
@vibeflui/core is version 1.1.0.
Core currently includes schema types, runtime validation, JSON Schema, schema normalization, FluiKit rendering components, reusable FluiKitCard and FluiKitTab primitives, TanStack Table-powered tables, form rendering with tabs, wizard steps, groups, and sections, multi-resource pages through resources[], action execution, endpoint and handler integration, registry-based customization, Tailwind defaults, TailAdmin preset foundation, message resolution, inspector utilities, plugin foundations, examples, and basic unit tests.
Optional ecosystem packages ship working renderers and adapters:
@vibeflui/react-query@vibeflui/tailadmin@vibeflui/shadcn@vibeflui/react-select@vibeflui/mantine@vibeflui/antd@vibeflui/material-ui@vibeflui/editors
OpenAPI Generator and FluiKit Studio are planned roadmap items, not current runtime features.
Installation
npm install @vibeflui/corepnpm add @vibeflui/coreyarn add @vibeflui/corePeer dependencies:
npm install react react-domCore dependencies include @tanstack/react-table, @iconify/react, and zod. TanStack Query is optional through @vibeflui/react-query; it is not required by core.
CSS Setup
FluiKit's Tailwind utility classes live inside the compiled @vibeflui/* package output under node_modules, not in your own source files. Tailwind only generates CSS for classes it can actually see, so you must point it at the package output — otherwise FluiKit renders with no styling.
Tailwind v4 (current)
Add an @source directive next to your @import "tailwindcss"; line, in whichever CSS file bootstraps Tailwind for your app:
/* app/globals.css */
@import "tailwindcss";
@source "../node_modules/@vibeflui";Add one more @source line per adapter package you install, for example:
@source "../node_modules/@vibeflui/shadcn";
@source "../node_modules/@vibeflui/tailadmin";Adjust the relative path to match where your CSS entry file actually lives in your project.
Tailwind v3
Add the package output to the content array in tailwind.config.ts:
// tailwind.config.ts
export default {
content: [
"./app/**/*.{ts,tsx}",
"./node_modules/@vibeflui/**/*.{js,ts,jsx,tsx}"
]
}Quick Start
Create a schema:
import type { FluiKitConfig } from "@vibeflui/core"
export const userSchema = {
resource: "users",
endpoint: "/api/users",
table: {
columns: ["name", "email", "role.name", "status"]
},
form: {
fields: ["name", "email", "roleId"]
},
actions: true
} satisfies FluiKitConfigRender it:
"use client"
import { FluiKit } from "@vibeflui/core"
import { userSchema } from "./user.schema"
export function UsersPage() {
return <FluiKit schema={userSchema} />
}What Gets Generated?
A schema can generate:
- Table.
- Search.
- Filters.
- Pagination.
- Row number column.
- Detail view.
- Form tabs, wizard steps, groups, and sections.
- Multi-resource pages.
- Dashboard cards.
- Create form.
- Edit form.
- Delete confirmation.
- Custom row actions.
- Toolbar actions.
- Bulk action foundations.
- Loading, empty, and error states.
The Add Data button is generated in the top-right toolbar when actions.create.enabled is true. The default label is add data, and you can override it in schema.
Architecture Overview
Schema
↓
Validator
↓
Normalizer
↓
Registry
↓
Provider
↓
Renderer
↓
FluiKit UI| Layer | Responsibility |
| --- | --- |
| Schema | JSON-friendly resource configuration for tables, forms, fields, actions, endpoints, messages, feedback, permissions, and theme options. |
| Validator | Runtime validation through Zod and editor/tool validation through JSON Schema. |
| Normalizer | Expands compact AI-friendly schema into normalized FluiKit config. |
| Registry | Resolves string keys for components, renderers, validators, transformers, handlers, events, options, permissions, classes, and messages. |
| Provider | Keeps backend execution outside visual components through handlers > provider > endpoint. |
| Renderer | Renders forms, tables, actions, modals, feedback, detail surfaces, dashboard cards, tabs, and fields. |
| FluiKit UI | The generated user interface. |
Schema Example
import type { FluiKitConfig } from "@vibeflui/core"
export const usersSchema = {
version: "1.0.0",
resource: "users",
title: "Users",
description: "User management",
mode: "server",
identity: {
key: "id",
param: "id"
},
endpoint: {
list: "/api/users",
create: "/api/users",
update: {
url: "/api/users/:id",
method: "PATCH"
},
delete: "/api/users/:id"
},
table: {
mode: "server",
dataPath: "data.items",
totalPath: "data.total",
search: {
enabled: true,
placeholder: "Search users...",
param: "q"
},
filters: [
{
key: "status",
label: "Status",
type: "select",
options: [
{ label: "Active", value: "active" },
{ label: "Pending", value: "pending" },
{ label: "Suspended", value: "suspended" }
]
}
],
pagination: {
enabled: true,
pageSize: 10,
pageSizeParam: "limit"
},
columns: [
{ key: "name", label: "Name", sortable: true, searchable: true },
{ key: "email", label: "Email" },
{ key: "role.name", label: "Role" },
{ key: "status", label: "Status", type: "custom", renderer: "StatusBadge" }
]
},
form: {
mode: "modal",
width: "2xl",
grid: {
columns: { base: 1, md: 2 },
gap: "gap-4"
},
fields: [
{ name: "name", label: "Name", type: "text" },
{ name: "email", label: "Email", type: "email" },
{ name: "roleId", label: "Role", type: "select", valueFrom: "role.id" },
{ name: "password", label: "Password", type: "password", editValueMode: "empty" }
]
},
detail: {
enabled: true,
title: "User Detail",
width: "3xl",
grid: { columns: { base: 1, md: 2 } },
fields: ["id", "name", "email", "role.name", "status"]
},
actions: {
create: { enabled: true, label: "Add User" },
edit: { enabled: true, label: "Edit" },
delete: { enabled: true, label: "Delete", confirm: true },
view: { enabled: true, label: "Detail" },
row: [
{
key: "approve",
label: "Approve",
icon: "mdi:check-circle-outline",
tooltip: "Approve user",
display: "icon",
endpoint: "/api/users/:id/approve",
method: "POST",
visibleWhen: {
key: "status",
operator: "equals",
value: "pending"
}
}
]
}
} satisfies FluiKitConfigTable Mapping
Table columns can map directly to backend response fields:
table: {
columns: [
{ key: "name", label: "Name" },
{ key: "email", label: "Email" },
{ key: "role.name", label: "Role" }
]
}Nested paths are supported:
{
key: "role.name"
}Also supported:
{ key: "user.profile.fullName" }
{ key: "metadata.status" }Backend response mapping is supported through dataPath and totalPath:
table: {
dataPath: "data.items",
totalPath: "data.total"
}Wide tables can opt into a minimum table width and horizontal scrolling:
table: {
minWidth: "960px",
columns: [
{ key: "name", label: "Name", width: "40%" },
{ key: "sku", label: "SKU", width: 20 },
{ key: "status", label: "Status" }
]
}minWidth keeps the wrapper constrained to max-width: 100% while the table scrolls horizontally. Column width accepts a percentage string or number. When any visible data column uses a percentage width, visible data columns without width automatically share the remaining percentage. Internal row number, action, and selection columns reserve fixed utility widths so percentage data columns are calculated from the remaining space.
In server mode, load failures keep the table wrapper and headers visible. The resolved load error message is rendered inside a full-width table body row.
Mutation Feedback
After create, update, delete, bulk, toolbar, or custom row actions, FluiKit can show success, error, or warning feedback from the backend response.
Recommended backend mutation response. status is the only required field; code and message improve tone and message resolution:
{ status: true, code: 201, message: "User created successfully.", data: { id: "usr_001" } }{ status: false, code: 422, message: "Email is already registered." }{ status: false, code: 500, message: "Unable to delete the user." }status: true renders success feedback. status: false with a 4xx code renders warning feedback, and status: false with a 5xx code renders error feedback. The message value goes through the shared message pipeline before falling back to English defaults.
Feedback is modal by default and can be switched to inline alerts:
feedback: {
mode: "inline",
icons: {
success: "mdi:check-circle-outline",
error: "mdi:alert-circle-outline",
warning: "mdi:alert-outline"
}
}Inline feedback uses Tailwind alert colors: green for success, red for error, and orange for warning. Feedback icons use Iconify keys; @iconify/react is already included by @vibeflui/core.
Edit Mapping
Use valueFrom when the edit form field name differs from the row path:
form: {
fields: [
{
name: "roleId",
label: "Role",
type: "select",
valueFrom: "role.id"
}
]
}Empty Edit Values
Use editValueMode: "empty" for fields that should not reuse row values during edit:
form: {
fields: [
{
name: "password",
label: "Password",
type: "password",
editValueMode: "empty"
}
]
}Identity Keys
Identity config controls how row actions resolve path params:
identity: {
key: "id"
}Custom param name:
identity: {
key: "uuid",
param: "userId"
}Nested identity:
identity: {
key: "user.id",
param: "userId"
}Custom Actions
actions: {
row: [
{
key: "approve",
label: "Approve",
icon: "mdi:check-circle-outline",
tooltip: "Approve user",
display: "icon",
handler: "approveUser",
visibleWhen: {
key: "status",
operator: "equals",
value: "pending"
}
}
]
}Built-in table row actions use borderless Iconify icon-only buttons with tooltips, accessible labels, hover feedback, and | separators between visible icons. Delete keeps a red danger color. By default, enabled actions include detail, edit, and delete. Detail renders a read-only label/value surface with an underline layout, not disabled inputs.
When a row has more than three visible actions, FluiKit keeps up to two priority actions visible and groups the rest behind a lucide:list-collapse icon menu by default. The priority order is view/detail, update/edit, then delete; custom actions fill any remaining direct slot in declaration order. Disable grouping with table.actionGrouping: false.
Customize the visible row action hover effect with table.actionIcon:
table: {
actionIcon: {
hoverShape: "circle",
hoverColor: "brand",
hoverClassName: "hover:ring-2 hover:ring-brand-200 dark:hover:ring-brand-700"
}
}hoverShape can be "rounded" or "circle" and defaults to "rounded". hoverColor defaults to "gray" and supports gray, brand, blue, green, orange, red, success, warning, danger, and error. Use hoverClassName when the app needs a custom Tailwind hover class.
Actions with confirm render a styled FluiKit confirmation dialog (not a native browser prompt). Delete and danger-variant actions use a danger tone automatically. Customize the copy through confirm:
{
key: "delete",
label: "Delete",
confirm: {
title: "Delete user",
description: "This permanently removes the user.",
icon: "mdi:alert-circle-outline",
confirmLabel: "Delete",
cancelLabel: "Keep",
secondConfirm: {
title: "Delete user permanently",
description: "This user has linked audit history. Confirm one more time before deleting.",
confirmLabel: "Delete permanently",
icon: "mdi:shield-alert-outline"
}
}
}secondConfirm is optional. Use secondConfirm: true for the default second-step copy, or provide an object with custom text and icon.
Custom Components
Schema stores registry keys, not inline functions:
form: {
fields: [
{
name: "avatar",
label: "Avatar",
type: "custom",
component: "AvatarUploader"
}
]
}Runtime supplies the component:
import { FluiKit, FluiKitProvider } from "@vibeflui/core"
import { AvatarUploader } from "./AvatarUploader"
<FluiKitProvider
components={{
AvatarUploader
}}
>
<FluiKit schema={schema} />
</FluiKitProvider>Custom table cells work the same way:
table: {
columns: [
{
key: "status",
label: "Status",
type: "custom",
renderer: "StatusBadge"
}
]
}<FluiKitProvider
renderers={{
StatusBadge
}}
>
<FluiKit schema={schema} />
</FluiKitProvider>Registry Pattern
VibeFlui keeps schemas JSON-serializable.
Use this:
{
renderer: "StatusBadge"
}Not this:
{
renderer: (row) => <StatusBadge status={row.status} />
}Registry string keys are important because they are:
- JSON serializable.
- AI friendly.
- Easier to validate.
- Easier to export.
- Safer to store.
- Easier to share between tools.
- Ready for visual builders and generators.
The runtime registry supports:
- Components.
- Renderers.
- Field adapters.
- Validators.
- Transformers.
- Action handlers.
- Permission resolvers.
- Option loaders.
- Hooks.
- Events.
- Class name resolvers.
- Message resolvers.
- Computed resolvers.
Computed Fields
Computed fields use a computed registry key and can declare dependencies with computedFrom:
form: {
fields: [
{ name: "title", label: "Title", type: "text" },
{ name: "slug", label: "Slug", type: "text", computed: "slugFromTitle", computedFrom: "title", readonly: true }
]
}<FluiKitProvider
computedResolvers={{
slugFromTitle: ({ values }) => String(values.title ?? "").toLowerCase().replace(/\s+/g, "-")
}}
>
<FluiKit schema={schema} />
</FluiKitProvider>computedFrom may be a string or string array. FluiKit uses it as a dependency list: the resolver runs initially, reruns when one of those dependencies changes, and preserves the previous computed value when unrelated fields change.
Message Resolvers
messages.resolver points to a registry message resolver. Resolvers receive the message key plus runtime context when available:
<FluiKitProvider
messageResolvers={{
resolveAdminMessage: (key, context) => {
if (key === "confirmDeleteDescription") return `Delete ${context.row?.name}?`
return undefined
}
}}
>
<FluiKit schema={schema} />
</FluiKitProvider>Depending on the call site, context can include schema, row, rows, values, action, mode, query, and value. Backend messages and schema defaults still follow messages.sourcePriority.
Backend Integration
FluiKit is backend-agnostic. It does not own your persistence, authorization, final validation, workflow, or audit log.
Execution priority:
handlers > provider > endpointEndpoint Mode
endpoint: {
list: "/api/users",
create: "/api/users",
update: {
url: "/api/users/:id",
method: "PATCH"
},
delete: {
url: "/api/users/:id",
method: "DELETE"
}
}Handler Mode
Schema uses handler keys:
handlers: {
create: "createUser",
update: "updateUser",
delete: "deleteUser"
}Runtime provides the functions:
<FluiKitProvider
handlers={{
createUser: async ({ values }) => api.users.create(values),
updateUser: async ({ identityValue, values }) => api.users.update(identityValue, values),
deleteUser: async ({ identityValue }) => api.users.delete(identityValue)
}}
>
<FluiKit schema={schema} />
</FluiKitProvider>Provider Mode
<FluiKitProvider
providers={{
adminApi: {
list: async ({ query }) => api.users.list(query),
create: async ({ values }) => api.users.create(values),
update: async ({ identityValue, values }) => api.users.update(identityValue, values),
delete: async ({ identityValue }) => api.users.delete(identityValue)
}
}}
>
<FluiKit schema={schema} />
</FluiKitProvider>provider: {
type: "custom",
name: "adminApi"
}Supported Field Types
| Field Type | Status |
| --- | --- |
| text | ✅ Native |
| email | ✅ Native |
| password | ✅ Native |
| number | ✅ Native |
| textarea | ✅ Native |
| select | ✅ Native |
| multiselect | ✅ Native |
| checkbox | ✅ Native checkbox |
| checkbox-group | ✅ Native multiple selection |
| radio | ✅ Native single selection |
| radio-group | ✅ Native single selection |
| switch | ✅ Native sliding toggle |
| date | ✅ Native |
| datetime | ✅ Native |
| time | ✅ Native |
| file | ✅ Native |
| image | ✅ Native file input with image accept |
| hidden | ✅ Native |
| color | ✅ Native |
| range | ✅ Native |
| json | ✅ Monospace textarea (or Editor.js block editor via @vibeflui/editors, adapter: "editorjs" — full tool set via buildEditorJsTools, see the editors package README) |
| code | ✅ Monospace textarea (CodeMirror via @vibeflui/editors) |
| markdown | ✅ Textarea (live editor via @vibeflui/editors) |
| richtext | ✅ Textarea (TipTap WYSIWYG via @vibeflui/editors) |
| tags | ✅ Comma-separated input |
| combobox | ✅ Native datalist |
| autocomplete | ✅ Native datalist |
| custom | ✅ Registry component |
Rich third-party controls can be supplied through fieldAdapters.
Use switch for toggle, on-off, and slide controls. Use checkbox for a normal checkbox, checkbox-group for multiple selections, and radio or radio-group for single selection from options. checkbox-toggle is not an official field type.
Field Validation
Common validation rules are built in and run automatically on submit — no custom validator required:
form: {
fields: [
{ name: "name", label: "Name", type: "text", required: true, minLength: 2, maxLength: 80 },
{ name: "email", label: "Email", type: "email", required: true },
{ name: "age", label: "Age", type: "number", min: 18, max: 99 },
{ name: "startDate", label: "Start date", type: "date", min: "2024-01-01", max: "2024-12-31" },
{ name: "code", label: "Code", type: "text", pattern: "^[A-Z]{3}$", validationMessage: "Use 3 uppercase letters." }
]
}Supported rules: required, minLength, maxLength, min, max, pattern, plus automatic format checks for email fields. min/max work with numeric fields and date-like date/datetime fields; date bounds can be ISO-style strings such as "2024-01-01" or "2024-01-01T09:00". Required fields render a * marker in the label. Messages use English defaults with {label}/{min}/{max} interpolation and can be overridden per field with validationMessage, or globally through messages.defaults.
Built-in rules run first; a registry validator key still runs afterwards for advanced or async checks. The standalone helper is also exported:
import { validateFieldValue } from "@vibeflui/core"
const error = validateFieldValue({ field, value })Theme System
Default styling is Tailwind CSS.
Supported today:
- Tailwind default preset.
- TailAdmin preset foundation.
- Dark-mode-aware default classes for tables, forms, dialogs, dashboard cards, and native inputs.
- Custom Tailwind class slots.
- Tailwind-aware
cn()helper.
Package foundations exist for:
- Shadcn.
- Mantine.
- Ant Design.
- Material UI.
- React Select field adapter.
- Editors field adapters for TipTap, CodeMirror, react-md-editor, and Editor.js.
React Select and editor adapters include dark-mode-aware runtime styling. Native select/date inputs use Tailwind dark classes plus color-scheme hints so browser controls can follow the active theme.
Example:
theme: {
preset: "default",
engine: "tailwind",
classNameMergeStrategy: "merge",
classNames: {
root: "space-y-6",
tableWrapper: "rounded-lg border border-gray-200 bg-white",
actionButton: "whitespace-nowrap"
}
}TailAdmin:
theme: {
preset: "tailadmin",
engine: "tailwind"
}Custom adapter direction:
adapter: {
ui: "custom",
form: "custom",
table: "tailwind",
modal: "custom"
}See the CSS Setup section above for the Tailwind v4 @source directive (or the Tailwind v3 content array) required so Tailwind generates CSS for these classes.
TypeScript
VibeFlui exports TypeScript types for schema-driven development:
import type {
FluiKitActionConfig,
FluiKitConfig,
FluiKitDataProviderConfig,
FluiKitDebugConfig,
FluiKitFieldConfig,
FluiKitFormConfig,
FluiKitIdentityConfig,
FluiKitLayoutConfig,
FluiKitMessagesConfig,
FluiKitComponentRegistry,
FluiKitRegistryConfig,
FluiKitTableConfig,
FluiKitThemeConfig
} from "@vibeflui/core"Use satisfies FluiKitConfig for great autocomplete while preserving literal schema types:
export const schema = {
resource: "products",
endpoint: "/api/products",
table: {
columns: ["name", "price", "status"]
},
form: {
fields: ["name", "price", "status"]
}
} satisfies FluiKitConfigJSON Schema
VibeFlui ships a JSON Schema:
schemas/fluikit.schema.jsonIt is used for:
- Validation.
- VS Code autocomplete.
- AI schema generation.
- Documentation.
- Future visual builders.
- Future OpenAPI generation workflows.
Package export:
import schemaJson from "@vibeflui/core/schemas/fluikit.schema.json"Zod Validation
Runtime validation is available through Zod-backed utilities:
import { validateSchema } from "@vibeflui/core"
const result = validateSchema(schema)
if (!result.valid) {
console.error(result.errors)
}The Zod schema is also exported:
import { fluiKitConfigSchema } from "@vibeflui/core"
const parsed = fluiKitConfigSchema.parse(schema)Normalizer
Compact AI-friendly schemas are normalized into full FluiKit config:
import { normalizeSchema } from "@vibeflui/core"
const normalized = normalizeSchema({
resource: "users",
endpoint: "/api/users",
table: {
columns: ["name", "email", "role.name"]
},
form: {
fields: ["name", "email"]
}
})Generate Resource Helper
import { generateResource } from "@vibeflui/core"
const usersSchema = generateResource({
resource: "users",
endpoint: "/api/users"
})Dashboard Cards
Render KPI/stat cards above the table from schema. Each card resolves its value from the list response via valueFrom, or fetches its own endpoint:
const schema = {
resource: "orders",
endpoint: "/api/orders",
dashboard: {
enabled: true,
title: "Overview",
cards: [
{ key: "total", label: "Total Orders", valueFrom: "stats.total", format: "number", icon: "mdi:cart" },
{ key: "revenue", label: "Revenue", valueFrom: "stats.revenue", format: "currency" },
{ key: "growth", label: "Growth", valueFrom: "stats.growth", format: "percent" },
{ key: "pending", label: "Pending", endpoint: "/api/orders/pending-count" }
]
},
table: { columns: ["orderNumber", "status", "total"] }
} satisfies FluiKitConfigFormats: number, currency, percent, text, custom. Supply card.renderer (or dashboard.renderer) registry keys for fully custom cards. <FluiKit /> renders the dashboard automatically when dashboard is present, including dashboard-only schemas and schemas that also define resources[]. Card endpoints use the configured custom provider when available, then fall back to the REST endpoint helper. <FluiKitDashboard /> can also be used standalone.
For landing pages, feature grids, and custom dashboard surfaces, use <FluiKitCard /> directly with Iconify icons:
import { FluiKitCard } from "@vibeflui/core"
<FluiKitCard
icon="mdi:rocket-launch-outline"
title="Developer First"
description="Type-safe, composable, and easy to extend."
/>Layout, Sections, and Resources
FluiKit supports schema-driven form layouts and multi-resource pages today.
Use layout.type: "tabs" to split a form into tabs:
const schema = {
resource: "users",
layout: {
type: "tabs",
tabs: [
{ key: "profile", title: "Profile", fields: ["name", "email"] },
{ key: "access", title: "Access", fields: ["role", "active"] }
]
},
form: {
fields: ["name", "email", "role", "active"]
}
} satisfies FluiKitConfigUse layout.type: "wizard" for step-by-step forms:
layout: {
type: "wizard",
steps: [
{ key: "basic", title: "Basic", fields: ["name", "email"] },
{ key: "security", title: "Security", fields: ["password", "role"] }
]
}Use form.sections when you want grouped fieldsets without changing the whole page layout:
form: {
sections: [
{ key: "basic", title: "Basic Information", fields: ["name", "email"] },
{ key: "settings", title: "Settings", fields: ["role", "active"] }
],
fields: ["name", "email", "role", "active"]
}Use resources[] for a multi-resource page:
const adminSchema = {
resource: "admin",
title: "Admin Console",
layout: { type: "tabs" },
dashboard: {
enabled: true,
cards: [{ key: "usersTotal", label: "Users", valueFrom: "meta.usersTotal" }]
},
table: { columns: ["name", "email", "status"] },
form: { fields: ["name", "email", "status"] },
resources: [rolesSchema, permissionsSchema]
} satisfies FluiKitConfigWhen resources[] is present, layout.type: "tabs" renders the root table/form as the first tab when the root has table/form UI, then appends child resources. layout.type: "sections" or "page" stacks the root resource and child resources as sections. Dashboard cards render above either layout.
layout.className applies to the root layout container regardless of layout.type:
layout: { className: "space-y-8" }For a single-resource screen, layout.sections or layout.tabs entries marked table: true or form: true place the resource table or an always-visible create form into that section/tab:
layout: {
type: "sections",
sections: [
{ key: "list", title: "All Users", table: true },
{ key: "add", title: "Add User", form: true }
]
}Entries that only declare fields remain form layout metadata. If table.enabled is false but create action and form rendering are enabled, <FluiKit /> renders a standalone create trigger so modal, drawer, page, or inline form surfaces remain reachable.
For custom pages outside schema rendering, use the exported <FluiKitTab /> primitive:
import { FluiKitTab } from "@vibeflui/core"
<FluiKitTab
defaultValue="view"
items={[
{ key: "view", label: "View", icon: "mdi:view-dashboard-outline", content: <Preview /> },
{ key: "code", label: "Code", icon: "mdi:code-json", content: <Code /> }
]}
/>Readonly Mode
Use readonly mode when a resource should only display data:
const schema = {
resource: "auditLogs",
mode: "readonly",
dataMode: "server",
endpoint: "/api/audit-logs",
table: {
columns: ["actor.name", "event", "metadata.status", "createdAt"]
},
actions: true
}Readonly mode hides generated action UI, hides Add Data, disables bulk selection, and no-ops create/update/delete/custom mutation execution even if actions are forced.
Viewing isn't a mutation, so it's the one exception: set actions.view.enabled: true to keep the view/detail action available under readonly mode while create/edit/delete stay forced off.
const schema = {
resource: "auditLogs",
mode: "readonly",
detail: { enabled: true, fields: ["actor.name", "event", "metadata.status"] },
actions: {
view: { enabled: true }
},
table: {
columns: ["actor.name", "event", "metadata.status", "createdAt"]
}
}Plugin System
Core includes plugin foundations through:
import { createFluiKitPlugin } from "@vibeflui/core"Plugins can bundle registry values, providers, messages, theme presets, adapter metadata, and inspector panel metadata.
const plugin = createFluiKitPlugin({
name: "@vibeflui/plugin-example",
version: "1.0.0",
components: { AvatarUploader },
renderers: { StatusBadge },
providers: { adminApi },
messages: {
optionLoadError: "Unable to load options."
}
})<FluiKitProvider plugins={[plugin]}>
<FluiKit schema={schema} />
</FluiKitProvider>Current official package foundations:
| Package | Purpose |
| --- | --- |
| @vibeflui/react-query | TanStack Query-compatible provider plugin (cache, invalidation, mutation lifecycle). |
| @vibeflui/tailadmin | TailAdmin theme preset + StatusBadge renderer and Switch field adapter. |
| @vibeflui/shadcn | Shadcn theme preset + StatusBadge renderer and Switch field adapter. |
| @vibeflui/react-select | React Select field adapter (react-select) for select/multiselect/tags. |
| @vibeflui/mantine | Mantine field adapter wrapping Mantine inputs, select, switch, radio, and checkbox controls. |
| @vibeflui/antd | Ant Design field adapter wrapping antd inputs, select, switch, radio, and checkbox controls. |
| @vibeflui/material-ui | Material UI field adapter wrapping MUI TextField, select, switch, radio, and checkbox controls. |
| @vibeflui/editors | Rich-text (TipTap), code (CodeMirror), markdown (react-md-editor), and block editor (Editor.js) field adapters. |
Each adapter package depends on its UI library through optional peer dependencies, so installing @vibeflui/core never pulls in TanStack Query, React Select, Mantine, Ant Design, or Material UI. Register an adapter, then reference it from schema:
import { FluiKit, FluiKitProvider } from "@vibeflui/core"
import { mantinePlugin } from "@vibeflui/mantine"
<FluiKitProvider plugins={[mantinePlugin]}>
<FluiKit schema={schema} />
</FluiKitProvider>form: {
fields: [
{ name: "role", label: "Role", type: "select", adapter: "mantine", options: [/* ... */] }
]
}Roadmap integrations include deeper plugin packages, OpenAPI generation, more UI adapter packs, and FluiKit Studio.
AI Coding Examples
Prompt:
Generate FluiKit schema for User CRUD.
Fields:
- name
- email
- role
- status
Use /api/users.Prompt:
Generate FluiKit schema for Product CRUD.
Fields:
- name
- sku
- price
- stock
- category
- status
Use nested category.name in table and categoryId in form.Prompt:
Generate FluiKit schema from this OpenAPI endpoint shape.
GET /api/orders returns data.items and data.total.
Identity key is order.id.
Table columns: orderNumber, customer.name, status, total.
Form fields: customerId, status, notes.Instead of generating a full JSX page, AI generates a smaller schema that FluiKit renders consistently.
Token Efficiency
Traditional AI workflow:
Prompt
↓
Generate JSX
↓
Generate Table
↓
Generate Form
↓
Generate Actions
↓
Review a large diffFluiKit workflow:
Prompt
↓
Generate Schema
↓
FluiKit
↓
UILess tokens. Less code. Faster iterations. More consistent results.
Next.js Usage
FluiKit components are client components. In the Next.js App Router, keep server-only work in Server Components, server modules, route handlers, or backend APIs, then render FluiKit from a small client component.
// app/users/UsersClient.tsx
"use client"
import { FluiKit } from "@vibeflui/core"
import { usersSchema } from "./users.schema"
export function UsersClient({ response }: { response: unknown }) {
return <FluiKit schema={usersSchema} response={response} />
}Repository examples include:
examples/nextjs-basic
examples/nextjs-server-table
examples/nextjs-tailadmin
examples/nextjs-custom-handler
examples/nextjs-full-feature
examples/nextjs-ecosystem
examples/nextjs-custom-theme
examples/ecosystem-packagesRelease Checks
The repository includes local checks for the current package contract:
npm run typecheck
npm run typecheck:ecosystem
npm run test
npm run build:ecosystem
npm pack --dry-runCurrent unit coverage is a basic Node test suite for schema generation, validation, normalization, inspector debug output, nested path utilities, form value mapping, endpoint/action/message resolution, endpoint execution, plugin conflicts, and exported examples.
For AI Assistants
llms.txt is a single, self-contained schema reference written for LLMs. Paste it into Cursor, Claude, ChatGPT, or any AI coding tool so the assistant generates valid FluiKit schemas instead of hand-written CRUD JSX. It is also shipped inside the npm package at @vibeflui/core/llms.txt.
For web-based AI retrieval, start with the public AI Source Map:
Documentation
- Documentation Website
- AI Guide
- Schema Rules
- Schema RFC
- Architecture
- Roadmap
- Release Checklist
- Breaking Changes
Contributing
Contributions are welcome.
Good first contributions include:
- Bug reports.
- Documentation improvements.
- Schema examples.
- Field adapter examples.
- Theme preset improvements.
- Plugin package experiments.
- Pull requests for small, focused fixes.
Before opening a pull request, run:
npm run checkFor larger changes, please explain how the change preserves the core principles:
- Schema-first.
- AI-first.
- Backend-agnostic.
- Type-safe.
- Registry-based customization.
- Tailwind-first.
- Minimal JSX.
❤️ Support VibeFlui
If VibeFlui saves you development time or AI tokens, consider supporting the project.
Your sponsorship helps fund:
- New features.
- Documentation.
- Bug fixes.
- Plugin ecosystem.
- OpenAPI Generator.
- FluiKit Studio.
- Community support.
Support links:
Crypto — BNB Smart Chain (BEP20), one address for BTCB / ETH / SOL / USDT / USDC (BEP20-wrapped/bridged tokens):
0x353BcD84747B800283011c77EB3e1a6a4ED732A6⚠️ Only send by explicitly selecting the BEP20 / BNB Smart Chain network in your wallet or exchange — native BTC, native ETH-mainnet, or native SOL sent directly to this address will be permanently lost. See SPONSORSHIP.md for details.
Contributors
Thanks to everyone who reports issues, writes examples, improves docs, builds adapters, and helps shape VibeFlui.
License
Apache License 2.0. See LICENSE.
