BuzzForm
BuzzFormDocs

Schema

Learn how to define form schemas and infer TypeScript types from your field definitions.

Schema

A BuzzForm schema is the single source of truth for your form. It defines the fields, validation, and generates TypeScript types automatically.

Creating a Schema

Use defineSchema() to define your form:

import { defineSchema } from "@buildnbuzz/form-react";

const contactSchema = defineSchema({
  fields: [
    { type: "text", name: "name", label: "Full Name", required: true },
    { type: "email", name: "email", label: "Email", required: true },
    { type: "textarea", name: "message", label: "Message" },
  ],
});

Best Practice: Define schemas in separate files (e.g., lib/schemas/contact.ts) for reusability across forms and server actions. See File Organization below.

Type Inference

Use InferType to get TypeScript types from your schema's fields:

import { defineSchema, type InferType } from "@buildnbuzz/form-react";

const contactSchema = defineSchema({
  fields: [
    { type: "text", name: "name", label: "Full Name", required: true },
    { type: "email", name: "email", label: "Email", required: true },
    { type: "textarea", name: "message", label: "Message" },
  ],
});

type ContactData = InferType<typeof contactSchema.fields>;
// Result: { name: string, email: string, message?: string }

This is especially useful for server actions:

app/actions.ts
"use server";

import { contactSchema, type ContactData } from "@/lib/schemas/contact";
import { db } from "@/lib/db";

export async function submitContact(data: ContactData) {
  // data is fully typed!
  await db.contacts.create(data);
}

File Organization

For production apps, organize schemas in dedicated files:

lib/schemas/contact.ts
import { defineSchema, type InferType } from "@buildnbuzz/form-react";

export const contactSchema = defineSchema({
  title: "Contact Form",
  description: "Send us a message",
  fields: [
    { type: "text", name: "name", label: "Full Name", required: true },
    { type: "email", name: "email", label: "Email", required: true },
    { type: "textarea", name: "message", label: "Message", required: true },
  ],
});

export type ContactData = InferType<typeof contactSchema.fields>;

Then import in your components and actions:

app/contact-form.tsx
"use client";

import { contactSchema } from "@/lib/schemas/contact";
import { Form, FormContent, FormFields, FormSubmit } from "@/components/buzzform/form";
import { submitContact } from "./actions";

export function ContactForm() {
  return (
    <Form schema={contactSchema} onSubmit={submitContact}>
      <FormContent>
        <FormFields />
        <FormSubmit>Send Message</FormSubmit>
      </FormContent>
    </Form>
  );
}

Benefits of this pattern:

  • ✅ Single source of truth for schema
  • ✅ Reusable in forms, server actions, and API routes
  • ✅ Type inference available everywhere
  • ✅ Easy to maintain and update

React Overrides (UI)

When using @buildnbuzz/form-react, you can pass rich React elements (like tooltips, icons, or custom components) directly to label and description fields.

Import Source: To enable ReactNode support in your schemas, you must import defineSchema and InferType from @buildnbuzz/form-react instead of @buildnbuzz/form-core.

Using Tooltips in Labels

A common pattern is adding an info icon with a tooltip to a field label:

import { defineSchema } from "@buildnbuzz/form-react";
import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/ui/tooltip";
import { InfoIcon } from "lucide-react";

const schema = defineSchema({
  fields: [
    {
      type: "password",
      name: "password",
      label: (
        <span className="flex items-center gap-1.5">
          Password
          <Tooltip>
            <TooltipTrigger render={<InfoIcon className="size-4 text-muted-foreground cursor-help" />} />
            <TooltipContent>Must be at least 8 characters long.</TooltipContent>
          </Tooltip>
        </span>
      ),
      required: true,
    },
  ],
});

Base Field Properties

Every field has these common properties:

Prop

Type

Nested Data Structures

Use group fields to create nested objects:

import { defineSchema, type InferType } from "@buildnbuzz/form-react";

const profileSchema = defineSchema({
  fields: [
    { type: "text", name: "name", label: "Full Name" },
    {
      type: "group",
      name: "address",
      label: "Address",
      fields: [
        { type: "text", name: "street", label: "Street" },
        { type: "text", name: "city", label: "City" },
        { type: "text", name: "zip", label: "ZIP Code" },
      ],
    },
  ],
});

type ProfileData = InferType<typeof profileSchema.fields>;
// Result: { name: string, address?: { street: string, city: string, zip: string } }

Array Data

Use array fields for repeatable items:

const jobSchema = defineSchema({
  fields: [
    { type: "text", name: "name", label: "Name" },
    {
      type: "array",
      name: "workHistory",
      label: "Work History",
      minItems: 1,
      maxItems: 5,
      fields: [
        { type: "text", name: "company", label: "Company" },
        { type: "text", name: "role", label: "Role" },
        { type: "text", name: "startDate", label: "Start Date" },
      ],
    },
  ],
});

type JobData = InferType<typeof jobSchema.fields>;
// Result: { name: string, workHistory: Array<{ company: string, role: string, startDate: string }> }

Complete Example

Here's a comprehensive schema combining all features:

lib/schemas/user-profile.ts
import { defineSchema, type InferType } from "@buildnbuzz/form-react";

export const userProfileSchema = defineSchema({
  title: "User Profile",
  description: "Complete your profile information",
  fields: [
    // Basic fields
    { type: "text", name: "firstName", label: "First Name", required: true },
    { type: "text", name: "lastName", label: "Last Name", required: true },
    { type: "email", name: "email", label: "Email", required: true },
    
    // Nested group
    {
      type: "group",
      name: "address",
      label: "Address",
      fields: [
        { type: "text", name: "street", label: "Street" },
        { type: "text", name: "city", label: "City" },
        { type: "text", name: "country", label: "Country" },
      ],
    },
    
    // Array of items
    {
      type: "array",
      name: "skills",
      label: "Skills",
      minItems: 1,
      fields: [
        { type: "text", name: "name", label: "Skill Name" },
        { 
          type: "select", 
          name: "level", 
          label: "Level",
          options: ["Beginner", "Intermediate", "Advanced"],
        },
      ],
    },
  ],
});

export type UserProfileData = InferType<typeof userProfileSchema.fields>;
// {
//   firstName: string,
//   lastName: string,
//   email: string,
//   address?: { street: string, city: string, country: string },
//   skills: Array<{ name: string, level: string }>
// }

On this page