Schema Foundation
VibeFlui schemas are JSON-friendly configuration objects that describe how FluiKit should render UI for React and Next.js applications.
Schema Foundation is the shared language for the rest of the documentation. Forms, tables, actions, feedback, layout, registry, examples, adapters, and API reference pages should all use the concepts defined here.
What a schema owns
A VibeFlui schema owns UI description:
- Resource identity.
- Form fields.
- Table columns.
- Declarative actions.
- Layout structure.
- Feedback display.
- Endpoint references.
- Registry string keys.
- Theme and class name hooks.
- AI metadata for documentation or generation workflows.
What the backend owns
The backend owns application truth:
- Business logic.
- Authentication.
- Authorization.
- Database reads and writes.
- Final validation.
- Workflows.
- Audit logs.
- Security decisions.
The schema can describe UI intent, but it must not be treated as a security boundary.
Core schema rules
- Keep schemas JSON-friendly.
- Do not put inline functions inside schemas.
- Use registry string keys for renderers, components, validators, transformers, handlers, hooks, events, permissions, options, messages, class name resolvers, and computed values.
- Keep schemas separate from TSX files.
- Prefer practical resource-specific schema files such as
schemas/user.form.schema.ts. - Keep backend-only logic out of schemas.
Minimal shape
schemas/users/users.schema.ts
TS
export const userSchema = {
version: "1.0.0",
resource: "users",
endpoint: "/api/users",
table: {
columns: ["name", "email", "status"]
},
form: {
mode: "modal",
fields: ["name", "email", "status"]
},
actions: true
} as const;Full foundation example
schemas/users/users.schema.ts
TS
export const usersSchema = {
version: "1.0.0",
resource: "users",
title: "Users",
description: "Manage workspace users.",
mode: "server",
uiMode: "interactive",
identity: {
key: "id",
param: "id"
},
endpoint: {
list: { url: "/api/users", method: "GET" },
create: { url: "/api/users", method: "POST" },
update: { url: "/api/users/:id", method: "PATCH" },
delete: { url: "/api/users/:id", method: "DELETE" }
},
table: {
mode: "server",
engine: "tanstack-table",
dataPath: "data",
totalPath: "pagination.total",
minWidth: 960,
rowNumber: true,
columns: [
{ key: "name", label: "Name", sortable: true, searchable: true },
{ key: "email", label: "Email", type: "email", searchable: true },
{ key: "role.name", label: "Role" },
{ key: "status", label: "Status", type: "badge", renderer: "users.statusBadge" }
],
search: {
enabled: true,
placeholder: "Search users",
param: "q"
},
pagination: {
enabled: true,
page: 1,
pageSize: 10,
pageParam: "page",
pageSizeParam: "pageSize"
},
states: {
loading: { text: "Loading users..." },
empty: { text: "No users found." },
error: { text: "Users could not be loaded." }
}
},
form: {
enabled: true,
mode: "modal",
width: "2xl",
grid: {
columns: { base: 1, md: 2 },
gap: "gap-4"
},
fields: [
{ name: "name", label: "Name", type: "text", required: true, validator: "users.name" },
{ name: "email", label: "Email", type: "email", required: true, validator: "users.email" },
{ name: "roleId", label: "Role", type: "select", optionLoader: "roles.options" },
{ name: "status", label: "Status", type: "select", optionLoader: "users.statusOptions" }
],
sections: [
{ key: "account", title: "Account", fields: ["name", "email"] },
{ key: "access", title: "Access", fields: ["roleId", "status"] }
],
submitLabel: "Save user",
cancelLabel: "Cancel"
},
actions: {
create: { enabled: true, label: "Create user", icon: "lucide:plus", handler: "users.create" },
edit: { enabled: true, label: "Edit", icon: "lucide:pencil", handler: "users.update" },
delete: {
enabled: true,
label: "Delete",
icon: "lucide:trash-2",
variant: "danger",
handler: "users.delete",
confirm: {
title: "Delete user?",
description: "This action cannot be undone.",
confirmLabel: "Delete",
cancelLabel: "Cancel",
icon: "lucide:triangle-alert"
}
}
},
permissions: {
create: "users.canCreate",
update: "users.canUpdate",
delete: "users.canDelete"
},
messages: {
sourcePriority: ["backend", "schema", "resolver", "default"],
defaults: {
createSuccess: "User created successfully.",
updateSuccess: "User updated successfully.",
deleteSuccess: "User deleted successfully."
},
resolver: "users.messages"
},
feedback: {
enabled: true,
mode: "modal",
icons: {
success: "lucide:circle-check",
error: "lucide:circle-x",
warning: "lucide:triangle-alert"
}
},
registry: {
renderers: ["users.statusBadge"],
validators: ["users.name", "users.email"],
actionHandlers: ["users.create", "users.update", "users.delete"],
permissionResolvers: ["users.canCreate", "users.canUpdate", "users.canDelete"],
optionLoaders: ["roles.options", "users.statusOptions"],
messageResolvers: ["users.messages"]
}
} as const;