VVibeFlui

AI Token Benchmark

This benchmark compares the prompt/code size for one simple UI built in two ways:

  • Manual React TSX.
  • VibeFlui schema plus a small page file.

The example is intentionally small: a users table, a create form surface, and a delete action surface. The goal is not to benchmark runtime performance or full CRUD persistence. The goal is to show how much UI code an AI assistant may need to read or rewrite when making screen-level changes.

Method

The character count is the exact number of characters inside the code snippets below, excluding Markdown fences and path labels.

The token count is an estimate using:

TXT
estimated tokens = ceil(total characters / 4)

Actual token counts depend on the model tokenizer, language, whitespace, and surrounding prompt.

Result

ApproachFilesTotal charactersEstimated tokens
Manual React TSX11,869468
VibeFlui schema + page21,131283

In this small example, the VibeFlui version uses about 39% fewer characters in the UI code that the AI needs to inspect or regenerate.

Manual React TSX

app/users/page.tsx

TSX
"use client";

import { useState } from "react";

type User = {
  id: string;
  name: string;
  email: string;
  role: "admin" | "editor" | "viewer";
};

const initialUsers: User[] = [
  { id: "u_1", name: "Alya", email: "alya@example.com", role: "admin" },
  { id: "u_2", name: "Bima", email: "bima@example.com", role: "viewer" }
];

export default function UsersPage() {
  const [users, setUsers] = useState(initialUsers);
  const [name, setName] = useState("");
  const [email, setEmail] = useState("");
  const [role, setRole] = useState<User["role"]>("viewer");

  function addUser() {
    setUsers((current) => [
      ...current,
      { id: crypto.randomUUID(), name, email, role }
    ]);
    setName("");
    setEmail("");
    setRole("viewer");
  }

  function deleteUser(id: string) {
    setUsers((current) => current.filter((user) => user.id !== id));
  }

  return (
    <main>
      <section>
        <input value={name} onChange={(event) => setName(event.target.value)} />
        <input value={email} onChange={(event) => setEmail(event.target.value)} />
        <select value={role} onChange={(event) => setRole(event.target.value as User["role"])}>
          <option value="admin">Admin</option>
          <option value="editor">Editor</option>
          <option value="viewer">Viewer</option>
        </select>
        <button onClick={addUser}>Create user</button>
      </section>

      <table>
        <thead>
          <tr><th>Name</th><th>Email</th><th>Role</th><th /></tr>
        </thead>
        <tbody>
          {users.map((user) => (
            <tr key={user.id}>
              <td>{user.name}</td>
              <td>{user.email}</td>
              <td>{user.role}</td>
              <td><button onClick={() => deleteUser(user.id)}>Delete</button></td>
            </tr>
          ))}
        </tbody>
      </table>
    </main>
  );
}

VibeFlui schema + page

schemas/users/user.resource.schema.ts

TS
export const userResourceSchema = {
  version: "1.0.0",
  resource: "users",
  title: "Users",
  table: {
    columns: ["name", "email", "role"]
  },
  form: {
    fields: [
      { name: "name", label: "Name", required: true },
      { name: "email", label: "Email", type: "email", required: true },
      {
        name: "role",
        label: "Role",
        type: "select",
        options: [
          { label: "Admin", value: "admin" },
          { label: "Editor", value: "editor" },
          { label: "Viewer", value: "viewer" }
        ]
      }
    ]
  },
  actions: {
    create: { enabled: true, label: "Create user" },
    delete: { enabled: true, label: "Delete", variant: "danger", confirm: true }
  }
} as const;

app/users/page.tsx

TSX
"use client";

import { FluiKit } from "@vibeflui/core";
import { userResourceSchema } from "@/schemas/users/user.resource.schema";

const users = [
  { id: "u_1", name: "Alya", email: "alya@example.com", role: "admin" },
  { id: "u_2", name: "Bima", email: "bima@example.com", role: "viewer" }
];

export default function UsersPage() {
  return <FluiKit schema={userResourceSchema} data={users} />;
}

This VibeFlui snippet shows the screen contract only. Without onCreate/onDelete handlers (or a wired provider/endpoint), FluiKit still shows success feedback when create or delete runs — VibeFlui only owns the FE/UI layer, so with no backend call to inspect it assumes the operation completed rather than reporting an error. To persist created rows or deleted rows in local state, pass named onCreate and onDelete handlers from a helper file, as shown in the full examples.

How to read the result

The savings come from moving repeated UI structure into schema. For AI-assisted work, that means many changes can be expressed as smaller edits:

  • Add a field by changing form.fields.
  • Add a column by changing table.columns.
  • Add a built-in action by changing actions.
  • Keep custom runtime behavior in registry files when needed.

For larger CRUD screens, the difference usually becomes more visible because manually written JSX grows with every field, column, action, state branch, and conditional UI path.