VVibeFlui

Simple Form

Generated UI previewFORM

Fill this in and submit — it calls the registry-backed users.create handler below and shows the inline success feedback, exactly as described in the schema.

This tutorial renders a form with validation, options, and a registry-backed submit handler.

Use this when you want a focused form preview without a table.

Why FluiKitForm directly

The page file below renders FluiKitForm directly instead of <FluiKit>. This is deliberate: <FluiKit> only opens a form.mode: "page" surface from a Create trigger button (its stand-in for the table toolbar when table.enabled is false), so the form is hidden behind an extra click. That fits a resource page where creating is one of several actions, but not a dedicated route like app/users/new/page.tsx, where the form should already be visible the moment the user lands there. Use FluiKitForm directly (see Direct Usage) whenever a route's entire purpose is showing one form.

File structure

TXT
app/
  users/
    new/
      page.tsx
schemas/
  users/
    user.form.schema.ts
lib/
  vibeflui/
    registry.tsx

Schema file

schemas/users/user.form.schema.ts

TS
export const userFormSchema = {
  resource: "users",
  title: "Create user",
  identity: {
    key: "id",
    param: "id"
  },
  table: {
    enabled: false
  },
  form: {
    mode: "page",
    fields: [
      { name: "name", label: "Name", required: true, minLength: 2 },
      { name: "email", label: "Email", type: "email", required: true },
      {
        name: "role",
        label: "Role",
        type: "select",
        required: true,
        options: [
          { label: "Admin", value: "admin" },
          { label: "Editor", value: "editor" },
          { label: "Viewer", value: "viewer" }
        ]
      },
      { name: "active", label: "Active", type: "switch", defaultValue: true }
    ]
  },
  actions: {
    create: {
      enabled: true,
      label: "Save user",
      handler: "users.create"
    }
  },
  registry: {
    actionHandlers: ["users.create"]
  },
  feedback: {
    mode: "inline"
  }
} as const;

The schema references users.create by string. The actual function lives in the registry.

Registry file

lib/vibeflui/registry.tsx

TSX
import { createRegistry } from "@vibeflui/core";

export const vibefluiRegistry = createRegistry({
  actionHandlers: {
    "users.create": async ({ values }) => ({
      status: true,
      code: 200,
      message: `${String(values?.name ?? "User")} created successfully`,
      data: values
    })
  }
});

Page file

app/users/new/page.tsx

TSX
"use client";

import { useState } from "react";
import {
  FluiKitFeedback,
  FluiKitForm,
  FluiKitProvider,
  executeOperation,
  normalizeSchema,
  type FluiKitFeedbackMessage
} from "@vibeflui/core";
import { userFormSchema } from "@/schemas/users/user.form.schema";
import { vibefluiRegistry } from "@/lib/vibeflui/registry";

const schema = normalizeSchema(userFormSchema);

export default function NewUserPage() {
  const [feedback, setFeedback] = useState<FluiKitFeedbackMessage | null>(null);
  const handleSubmitUser = async (values: Record<string, unknown>) => {
    const result = await executeOperation({
      schema,
      operation: "create",
      values,
      registry: vibefluiRegistry
    });

    setFeedback({
      status: "success",
      title: "Saved",
      message: String((result.data as { message?: unknown } | undefined)?.message ?? "User saved")
    });
  };

  return (
    <FluiKitProvider registry={vibefluiRegistry}>
      <FluiKitFeedback
        schema={schema}
        mode="inline"
        feedback={feedback}
        onClose={() => setFeedback(null)}
      />
      <FluiKitForm
        schema={schema}
        mode="create"
        onSubmit={handleSubmitUser}
      />
    </FluiKitProvider>
  );
}

Expected submit response

JSON
{
  "status": true,
  "code": 200,
  "message": "Ada Lovelace created successfully"
}