BuzzForm
BuzzFormDocs

Quick Start

Build a complete working form with validation and conditional logic in under five minutes.

Quick Start

This guide walks you through building a real form from scratch. By the end you'll have a working contact form with validation, a conditional field, and typed submission data.

Make sure you've completed Installation before continuing.

Step 1: Define Your Schema

A schema is a plain object with a fields array. Each field has a type, a name, and optional configuration:

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

export const contactSchema = defineSchema({
  title: "Contact Form",
  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>;
// Type: { name: string, email: string, message: string }

Defining schemas in separate files (like lib/schemas/contact.ts) allows you to reuse them in forms, server actions, and API routes. See Schema for details.

Step 2: Create the Form

Render the form with the shadcn registry components. The schema drives rendering and validation:

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

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

export function ContactForm() {
  return (
    <Form
      schema={contactSchema}
      onSubmit={({ value }) => {
        // value is typed as ContactData
        toast("Message sent!", {
          description: `From: ${value.email}`,
        });
      }}
    >
      <FormContent>
        <FormFields />
        <FormSubmit>Send Message</FormSubmit>
      </FormContent>
    </Form>
  );
}

<FormFields /> renders all fields using the FieldRegistry from <FormProvider>. Use <FormSubmit /> for a submit button that auto-disables while submitting.

Step 3: Add Validation

BuzzForm auto-derives validation from field properties like required, minLength, etc. You can also add explicit validation rules:

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

export const contactSchema = defineSchema({
  title: "Contact Form",
  fields: [
    { 
      type: "text", 
      name: "name", 
      label: "Full Name", 
      required: true,
      minLength: 2,
    },
    { 
      type: "email", 
      name: "email", 
      label: "Email", 
      required: true,
    },
    { 
      type: "textarea", 
      name: "message", 
      label: "Message", 
      required: true,
      minLength: 10,
      maxLength: 500,
    },
  ],
});

Now the form validates:

  • Name must be at least 2 characters
  • Email must be valid format (auto-derived from type: "email")
  • Message must be between 10-500 characters

See Validation for the complete list of validators.

Step 4: Add Conditional Logic

Show a field only when certain conditions are met. The condition property uses a declarative AST — no callbacks:

lib/schemas/contact.ts
export const contactSchema = defineSchema({
  title: "Contact Form",
  fields: [
    { 
      type: "text", 
      name: "name", 
      label: "Full Name", 
      required: true,
      minLength: 2,
    },
    { 
      type: "email", 
      name: "email", 
      label: "Email", 
      required: true,
    },
    {
      type: "select",
      name: "subject",
      label: "Subject",
      options: ["General", "Support", "Sales", "Other"],
      required: true,
    },
    { 
      type: "textarea", 
      name: "message", 
      label: "Message", 
      required: true,
      minLength: 10,
      maxLength: 500,
    },
    // Only shown when subject is "Other"
    {
      type: "text",
      name: "otherSubject",
      label: "Please Specify",
      condition: { $data: "/subject", eq: "Other" },
      required: true,
    },
  ],
});

When subject is not "Other", the otherSubject field is completely removed from the form state.

Note: When using condition, you don't need to repeat required in the condition. The field is only present when the condition is met, so required: true only applies when visible.

Complete Example

Here's the complete form with all features:

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

export const contactSchema = defineSchema({
  title: "Contact Form",
  fields: [
    { 
      type: "text", 
      name: "name", 
      label: "Full Name", 
      placeholder: "John Doe",
      required: true,
      minLength: 2,
    },
    { 
      type: "email", 
      name: "email", 
      label: "Email", 
      placeholder: "john@example.com",
      required: true,
    },
    {
      type: "select",
      name: "subject",
      label: "Subject",
      options: ["General", "Support", "Sales", "Other"],
      required: true,
    },
    { 
      type: "textarea", 
      name: "message", 
      label: "Message", 
      placeholder: "How can we help?",
      required: true,
      minLength: 10,
      maxLength: 500,
      description: "Be as detailed as possible so we can help you better.",
    },
    {
      type: "text",
      name: "otherSubject",
      label: "Please Specify",
      condition: { $data: "/subject", eq: "Other" },
      required: true,
    },
  ],
});

export type ContactData = InferType<typeof contactSchema.fields>;
app/contact-form.tsx
"use client";

import { contactSchema, type ContactData } from "@/lib/schemas/contact";
import { Form, FormContent, FormFields, FormSubmit } from "@/components/buzzform/form";
import { toast } from "sonner";

export function ContactForm() {
  return (
    <Form
      schema={contactSchema}
      onSubmit={async ({ value }) => {
        // value is typed as ContactData
        await fetch("/api/contact", {
          method: "POST",
          body: JSON.stringify(value),
        });
        toast("Message sent!", {
          description: `From: ${value.email}`,
        });
      }}
    >
      <FormContent>
        <FormFields />
        <FormSubmit>Send Message</FormSubmit>
      </FormContent>
    </Form>
  );
}
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);
}

Next Steps

Now that you have a working form, explore these topics:

On this page