VVibeFlui

FluiKitForm

FluiKitForm renders form fields from a NormalizedFluiKitConfig.

Most pages use it through FluiKit. Direct usage is useful for local demos or custom flows that already normalize the schema.

Props

TS
type FluiKitFormProps = {
  schema: NormalizedFluiKitConfig;
  mode?: "create" | "edit" | "view";
  row?: Record<string, unknown>;
  initialValues?: Record<string, unknown>;
  className?: string;
  onSubmit?: (values, context) => void | Promise<void>;
  onCancel?: () => void;
};

Default behavior

FeatureBehavior
modeDefaults to create.
valuesBuilt from initialValues or mapRowToFormValues(row, schema.form.fields, mode).
view modeUses edit-style row mapping but renders fields read-only.
validationRuns built-in and registry validation through validateValuesWithRegistry.
computed valuesApplies applyComputedValues before validation and submit.
transformersApplies transformValuesWithRegistry before onSubmit.
submit labelform.submitLabel, otherwise Update in edit mode or Create in create mode.
cancel labelform.cancelLabel, otherwise Cancel.

Layout behavior

FluiKitForm renders fields in this priority:

  1. layout.type: "tabs" with layout.tabs
  2. layout.type: "wizard" with layout.steps
  3. form.groups
  4. form.sections
  5. all form.fields

Grouped or sectioned forms still render ungrouped fields after the configured groups or sections.

Wizard behavior

Wizard steps validate only the current step before moving forward. The final step submits the full resolved value object.

schemas/users/users.schema.ts

TS
export const usersSchema = {
  version: "1.0.0",
  resource: "users",
  layout: {
    type: "wizard",
    steps: [
      { key: "basic", title: "Basic Information", fields: ["name", "email"] },
      { key: "access", title: "Access", fields: ["roleId", "status"] }
    ]
  },
  form: {
    fields: [
      { name: "name", label: "Name", type: "text", required: true },
      { name: "email", label: "Email", type: "email", required: true },
      {
        name: "roleId",
        label: "Role",
        type: "select",
        options: [
          { label: "Admin", value: "admin" },
          { label: "Editor", value: "editor" }
        ]
      },
      {
        name: "status",
        label: "Status",
        type: "select",
        options: [
          { label: "Active", value: "active" },
          { label: "Pending", value: "pending" }
        ]
      }
    ]
  }
} as const;

app/users/create-form.tsx

TSX
"use client";

import { FluiKitForm, normalizeSchema } from "@vibeflui/core";
import { usersSchema } from "@/schemas/users/users.schema";

const schema = normalizeSchema(usersSchema);

export function CreateUserForm() {
  return (
    <FluiKitForm
      schema={schema}
      mode="create"
      onSubmit={async (values) => {
        await saveUser(values);
      }}
    />
  );
}

This example does not need a FluiKitProvider or registry because none of its fields reference a component, adapter, or optionLoader.

Theme slots

The form uses these theme slots:

  • form
  • fieldWrapper
  • label
  • input
  • error
  • actionButton