Form

Building forms with TanStack Form, Zod validation, and accessible form components.

Overview

The Form components provide a complete solution for building accessible, type-safe forms with TanStack Form. Features include real-time validation with Zod, async validation, field-level error handling, and a clean API that eliminates boilerplate.

Use FormBuilder for most cases

For most use cases, use Form Builder instead of raw form primitives. It renders forms from typed formDefs with built-in conditional logic, multi-step wizards, and automatic layout.

Usage

import { InputField, Button } from "@tilt-legal/cubitt-components/primitives";
import {
  useForm,
  revalidateLogic } from "@tanstack/react-form";
import { z } from "zod";
import { Form } from "@tilt-legal/cubitt-components/primitives";
// 1. Define your validation schema with Zod
const schema = z.object({
  email: z.string().email("Invalid email address"),
  password: z.string().min(8, "Password must be at least 8 characters"),
});

// 2. Create the form with TanStack Form
const form = useForm({
  defaultValues: {
    email: "",
    password: "",
  },
  validationLogic: revalidateLogic({
    mode: "submit",
    modeAfterSubmission: "change",
  }),
  validators: { onDynamic: schema },
  onSubmit: async ({ value }) => {
    console.log("Form submitted:", value);
  },
});

// 3. Use the component API (dual-prop: name or field)
return (
  <Form form={form} className="space-y-4 max-w-md">
    <InputField
      name="email"
      label="Email"
      type="email"
      placeholder="m@example.com"
      required
    />
    <InputField
      name="password"
      label="Password"
      type="password"
      placeholder="••••••••"
      required
    />
    <Button type="submit">Login</Button>
  </Form>
);

Form Components

Form

The root form component that provides context and handles submission.

<Form form={form} invalidAnimation="shake">
  {/* form fields */}
</Form>
PropTypeDescription
formAnyReactFormApiTanStack Form instance from useForm()
invalidAnimation"shake"Optional shake animation when form is invalid
invalidOverridebooleanOverride the computed invalid state
initialValidatebooleanRun validation on initial mount (default: false)
noValidatebooleanDisable browser validation (default: true)
classNamestringAdditional CSS classes

Features

  • Auto-submit handling: Automatically calls form.handleSubmit() on form submission
  • Validation state: Computes invalid state from TanStack Form field and form-level errors
  • Context provision: Provides form context to all child field components via useTanstackFormContext()
  • Animation support: Built-in shake animation for invalid forms
  • State attributes: Adds data-invalid, data-submitting, etc. for CSS hooks

FormField

Container for form fields with error message display. Usually you won't use this directly as field components include it automatically.

<FormField messages={errors} size="md" fieldName="username">
  {/* field content */}
</FormField>
PropTypeDescription
messagesstring[]Error messages
fieldNamestringField name for IDs
size"xs" | "sm" | "md" | "lg"Message size
classNamestringAdditional CSS

See all field components (e.g., InputField, SelectField, TagInputField) on the Fields page.

FormFieldSet

Groups related fields with an optional title.

<FormFieldSet title="Personal Information" fieldName="personal">
  <InputField name="firstName" label="First Name" />
  <InputField name="lastName" label="Last Name" />
</FormFieldSet>
PropTypeDescription
titleReact.ReactNodeFieldset title
messagesstring[]Group-level errors
fieldNamestringField name for IDs
size"xs" | "sm" | "default" | "lg"Message size
classNamestringAdditional CSS

Context Hook

Access the form instance from any child component:

import { useTanstackFormContext } from "@tilt-legal/cubitt-components/primitives";

function CustomField() {
  const form = useTanstackFormContext();
  
  return (
    <form.Field name="custom">
      {(field) => (
        <input
          value={field.state.value}
          onChange={(e) => field.handleChange(e.target.value)}
        />
      )}
    </form.Field>
  );
}

Examples

On this page