@cassa/ui-forms
Form primitives wired to React Hook Form and Zod. Every field handles validation state, error messages, and accessibility out of the box.
Import
import {
Input,
Textarea,
Select,
Checkbox,
Switch,
FormField,
FormInput,
FormTextarea,
FormSelect,
FormCheckbox,
FormSwitch,
} from '@cassa/ui-forms'Uncontrolled primitives
Use Input, Textarea, Select, etc. standalone — they accept native HTML attributes and forward refs.
<Input placeholder="Email" type="email" />
<Input placeholder="Error state" error />
<Textarea placeholder="Description" rows={4} />
<Select options={[{ value: 'admin', label: 'Admin' }, { value: 'user', label: 'User' }]} />
<Checkbox label="Accept terms" />
<Switch label="Enable notifications" />Wired to React Hook Form
The FormInput family wraps each primitive with FormField, binding it to RHF and showing Zod errors automatically.
'use client'
import { useForm } from 'react-hook-form'
import { zodResolver } from '@hookform/resolvers/zod'
import { z } from 'zod'
import { FormInput, FormSelect, FormCheckbox } from '@cassa/ui-forms'
import { Button } from '@cassa/ui'
const schema = z.object({
email: z.string().email('Valid email required'),
role: z.string().min(1, 'Select a role'),
terms: z.boolean().refine(v => v, 'You must accept the terms'),
})
export const MyForm = () => {
const { control, handleSubmit } = useForm({
resolver: zodResolver(schema),
defaultValues: { email: '', role: '', terms: false },
})
return (
<form onSubmit={handleSubmit(console.log)} className="space-y-4">
<FormInput
control={control}
name="email"
label="Email"
type="email"
placeholder="you@example.com"
/>
<FormSelect
control={control}
name="role"
label="Role"
options={[
{ value: 'admin', label: 'Admin' },
{ value: 'user', label: 'User' },
]}
/>
<FormCheckbox
control={control}
name="terms"
label="I accept the terms and conditions"
/>
<Button type="submit">Submit</Button>
</form>
)
}FormField (low-level)
For custom inputs, use FormField directly with a render prop:
<FormField
control={control}
name="bio"
label="Bio"
description="Tell us a bit about yourself."
render={({ field }) => <Textarea {...field} rows={4} />}
/>| Prop | Type | Default | Description |
|---|---|---|---|
| name* | string | — | Field name — must match a key in the Zod schema. |
| label* | string | — | Visible label text. |
| description | string | — | Helper text shown below the field. |
| control* | Control | — | React Hook Form control object. |
| render* | (field) => ReactNode | — | Render prop receiving the bound field. |
Input props
| Prop | Type | Default | Description |
|---|---|---|---|
| type | string | "text" | HTML input type. |
| placeholder | string | — | Placeholder text. |
| disabled | boolean | false | Disables the field. |
| error | boolean | false | Applies error ring styling. |
| ...rest | InputHTMLAttributes | — | All native input attributes are forwarded. |