Registry
The VibeFlui registry maps schema string keys to runtime behavior.
Schemas stay JSON-friendly. Runtime code owns functions, React components, validators, handlers, option loaders, permissions, message resolvers, computed values, and class name resolvers.
Why registry exists
Do not put functions inside schemas.
schemas/users/bad-user.schema.ts
export const badSchema = {
resource: "users",
form: {
fields: [
{
name: "email",
validator: (value: string) => value.includes("@")
}
]
}
};Use a registry key instead.
schemas/users/good-user.schema.ts
export const goodSchema = {
version: "1.0.0",
resource: "users",
form: {
fields: [
{
name: "email",
validator: "users.email"
}
]
}
} as const;Register the function in runtime code.
lib/vibeflui/registry.ts
import { createRegistry } from "@vibeflui/core";
export const registry = createRegistry({
validators: {
"users.email": (value) => {
return /@/.test(String(value)) ? undefined : "Enter a valid email address.";
}
}
});Runtime registry slots
| Slot | Used for |
|---|---|
components | Custom React components. |
renderers | Custom field, detail, or table cell renderers. |
fieldAdapters | Custom form field implementations. |
validators | Field validation functions. |
transformers | Value and response transformations. |
actionHandlers | Action execution handlers. |
handlers | Handler alias supported by runtime execution. |
permissionResolvers | Permission decisions. |
optionLoaders | Dynamic field or filter options. |
hooks | Lifecycle hooks. |
events | Event handlers. |
classNameResolvers | Dynamic class name resolution. |
messageResolvers | Message resolution. |
computedResolvers | Computed field and column values. |
Schema registry declaration
The schema can declare which registry keys it expects.
schemas/users/users.schema.ts
export const usersSchema = {
version: "1.0.0",
resource: "users",
registry: {
renderers: ["users.statusBadge"],
fieldAdapters: ["react-select"],
validators: ["users.email"],
transformers: ["users.trimString"],
actionHandlers: ["users.create", "users.delete"],
permissionResolvers: ["users.canDelete"],
optionLoaders: ["roles.options"],
hooks: ["users.beforeCreate"],
events: ["users.formSubmitSucceeded"],
classNameResolvers: ["users.tableRowClassName"],
messageResolvers: ["users.messages"],
computedResolvers: ["users.displayName"]
}
} as const;The declaration helps inspection and documentation. The runtime functions still belong in createRegistry() or FluiKitProvider.
Runtime setup
app/users/page.tsx
"use client";
import { FluiKit, FluiKitProvider, createRegistry } from "@vibeflui/core";
import { usersSchema } from "@/schemas/users.schema";
const registry = createRegistry({
validators: {
"users.email": (value) => {
return /@/.test(String(value)) ? undefined : "Enter a valid email address.";
}
},
optionLoaders: {
"roles.options": async () => [
{ label: "Admin", value: "admin" },
{ label: "Editor", value: "editor" }
]
},
actionHandlers: {
"users.create": async ({ values }) => {
return {
status: true,
code: 200,
message: `${String(values?.name ?? "User")} created`
};
}
}
});
export default function UsersPage() {
return (
<FluiKitProvider registry={registry}>
<FluiKit schema={usersSchema} />
</FluiKitProvider>
);
}Provider shortcut
FluiKitProvider can receive a registry object or individual registry slots.
app/users/page.tsx
<FluiKitProvider
validators={{
"users.email": (value) => {
return /@/.test(String(value)) ? undefined : "Enter a valid email address.";
}
}}
>
<FluiKit schema={usersSchema} />
</FluiKitProvider>For examples, prefer a named registry object because it is easier to reuse across pages.
Merge behavior
Registries are merged in order. Later registry entries override earlier entries with the same key.
This matters when combining parent providers, plugins, adapter packages, and page-specific registry entries.
Recommended file organization
schemas/
users.schema.ts
lib/
vibeflui/
registry.tsx
app/
users/
page.tsxKeep schemas declarative. Keep registry functions in runtime code.