Custom Fields and Adapters
VibeFlui supports two extension paths for field rendering:
field.componentfromregistry.componentsfield.adapterfromregistry.fieldAdapters
Field adapter
Adapters are recommended when the field still behaves like a form control.
components/vibeflui/fields/RoleSelectField.tsx
TSX
import type { FluiKitRuntimeComponentProps } from "@vibeflui/core";
export function RoleSelectField({ value, options = [], onChange }: FluiKitRuntimeComponentProps) {
return (
<select value={String(value ?? "")} onChange={(event) => onChange?.(event.target.value)}>
<option value="">Select role</option>
{options.map((option) => {
const item = option as { label?: string; value?: string | number | boolean };
return (
<option key={String(item.value)} value={String(item.value)}>
{String(item.label ?? item.value ?? "")}
</option>
);
})}
</select>
);
}lib/vibeflui/registry.tsx
TSX
import { createRegistry } from "@vibeflui/core";
import { RoleSelectField } from "@/components/vibeflui/fields/RoleSelectField";
export const registry = createRegistry({
fieldAdapters: {
"users.roleSelect": RoleSelectField
}
});TS
export const usersSchema = {
resource: "users",
form: {
fields: [
{
name: "roleId",
label: "Role",
type: "select",
adapter: "users.roleSelect",
optionLoader: "users.roles"
}
]
},
registry: {
fieldAdapters: ["users.roleSelect"],
optionLoaders: ["users.roles"]
}
} as const;Custom component
Use component for richer bespoke UI.
components/vibeflui/fields/AvatarUploader.tsx
TSX
type AvatarUploaderProps = {
value?: unknown;
onChange?: (value: File | undefined) => void;
};
export function AvatarUploader({ onChange }: AvatarUploaderProps) {
return (
<input
type="file"
accept="image/*"
onChange={(event) => onChange?.(event.target.files?.[0])}
/>
);
}TSX
import { createRegistry } from "@vibeflui/core";
import { AvatarUploader } from "@/components/vibeflui/fields/AvatarUploader";
export const registry = createRegistry({
components: {
AvatarUploader
}
});TS
{
name: "avatar",
type: "image",
component: "AvatarUploader"
}Missing custom component
When field.type is custom or field.component is set but the component is not registered, VibeFlui renders a dashed missing-component placeholder.
Props
Adapters and custom components receive:
schemafieldvaluevaluesmodeoptionsonChange