Skip to content

Form

React Hook Form integration with accessible labels and validation

Simple Form

Usage

tsx
import { Form, FormItem, FormLabel, FormControl, FormDescription, FormMessage } from '@tryvienna/ui'

<Form {...form}>
  <form onSubmit={form.handleSubmit(() => {})} className="grid gap-6 w-96">
    <FormField
      control={form.control}
      name="username"
      rules={{ required: 'Username is required' }}
      render={({ field }) => (
        <FormItem>
          <FormLabel>Username</FormLabel>
          <FormControl>
            <Input placeholder="johndoe" {...field} />
          </FormControl>
          <FormDescription>Your public display name.</FormDescription>
          <FormMessage />
        </FormItem>
      )}
    />
    <FormField
      control={form.control}
      name="email"
      rules={{
        required: 'Email is required',
        pattern: { value: /^[^\s@]+@[^\s@]+\.[^\s@]+$/, message: 'Invalid email address' },
      }}
      render={({ field }) => (
        <FormItem>
          <FormLabel>Email</FormLabel>
          <FormControl>
            <Input type="email" placeholder="john@example.com" {...field} />
          </FormControl>
          <FormDescription>We'll never share your email.</FormDescription>
          <FormMessage />
        </FormItem>
      )}
    />
    <FormField
      control={form.control}
      name="bio"
      render={({ field }) => (
        <FormItem>
          <FormLabel>Bio</FormLabel>
          <FormControl>
            <Textarea placeholder="Tell us about yourself..." {...field} />
          </FormControl>
          <FormDescription>Brief description for your profile.</FormDescription>
          <FormMessage />
        </FormItem>
      )}
    />
    <Button type="submit">Submit</Button>
  </form>
</Form>

Examples

Simple Form

Simple Form
tsx
<Form {...form}>
  <form onSubmit={form.handleSubmit(() => {})} className="grid gap-6 w-96">
    <FormField
      control={form.control}
      name="username"
      rules={{ required: 'Username is required' }}
      render={({ field }) => (
        <FormItem>
          <FormLabel>Username</FormLabel>
          <FormControl>
            <Input placeholder="johndoe" {...field} />
          </FormControl>
          <FormDescription>Your public display name.</FormDescription>
          <FormMessage />
        </FormItem>
      )}
    />
    <FormField
      control={form.control}
      name="email"
      rules={{
        required: 'Email is required',
        pattern: { value: /^[^\s@]+@[^\s@]+\.[^\s@]+$/, message: 'Invalid email address' },
      }}
      render={({ field }) => (
        <FormItem>
          <FormLabel>Email</FormLabel>
          <FormControl>
            <Input type="email" placeholder="john@example.com" {...field} />
          </FormControl>
          <FormDescription>We'll never share your email.</FormDescription>
          <FormMessage />
        </FormItem>
      )}
    />
    <FormField
      control={form.control}
      name="bio"
      render={({ field }) => (
        <FormItem>
          <FormLabel>Bio</FormLabel>
          <FormControl>
            <Textarea placeholder="Tell us about yourself..." {...field} />
          </FormControl>
          <FormDescription>Brief description for your profile.</FormDescription>
          <FormMessage />
        </FormItem>
      )}
    />
    <Button type="submit">Submit</Button>
  </form>
</Form>

With Validation Errors

With Validation Errors
tsx
<Form {...form}>
  <form className="grid gap-6 w-96">
    <FormField
      control={form.control}
      name="name"
      rules={{
        required: 'Name is required',
        minLength: { value: 2, message: 'Min 2 characters' },
      }}
      render={({ field }) => (
        <FormItem>
          <FormLabel>Name</FormLabel>
          <FormControl>
            <Input placeholder="Your name" {...field} />
          </FormControl>
          <FormMessage />
        </FormItem>
      )}
    />
    <FormField
      control={form.control}
      name="email"
      rules={{
        required: 'Email is required',
        pattern: { value: /^[^\s@]+@[^\s@]+\.[^\s@]+$/, message: 'Invalid email address' },
      }}
      render={({ field }) => (
        <FormItem>
          <FormLabel>Email</FormLabel>
          <FormControl>
            <Input type="email" {...field} />
          </FormControl>
          <FormMessage />
        </FormItem>
      )}
    />
    <Button type="submit">Submit</Button>
  </form>
</Form>

With Checkbox

With Checkbox
tsx
<Form {...form}>
  <form onSubmit={form.handleSubmit(() => {})} className="grid gap-6 w-96">
    <FormField
      control={form.control}
      name="terms"
      rules={{ validate: (v) => v === true || 'You must accept the terms' }}
      render={({ field }) => (
        <FormItem>
          <div className="flex items-center gap-2">
            <FormControl>
              <Checkbox checked={field.value} onCheckedChange={field.onChange} />
            </FormControl>
            <FormLabel>Accept terms and conditions</FormLabel>
          </div>
          <FormDescription>
            You agree to our Terms of Service and Privacy Policy.
          </FormDescription>
          <FormMessage />
        </FormItem>
      )}
    />
    <Button type="submit">Continue</Button>
  </form>
</Form>

API Reference

FormItem

Also accepts all props from div.

FormLabel

Also accepts all props from LabelPrimitive.Root.

FormControl

Also accepts all props from Slot.

FormDescription

Also accepts all props from p.

FormMessage

Also accepts all props from p.

Data Attributes

Componentdata-slot
Form"form-item"
FormItem"form-item"
FormLabel"form-label"
FormControl"form-control"
FormDescription"form-description"
FormMessage"form-message"