Founding price: $35, then $79.99 after 23:59 UTC on 4 November, then $79.99
Get ProForms are where accessibility matters most: every input needs a label, help
text must be announced, and errors have to reach people who can't see red. The
Field primitives handle that wiring.
"use client"
import { zodResolver } from "@hookform/resolvers/zod"
import { Controller, useForm } from "react-hook-form"
import { z } from "zod"
import { Button } from "@/components/ui/button"
import {
Field,
FieldDescription,
FieldError,
FieldGroup,
FieldLabel,
} from "@/components/ui/field"
import { Input } from "@/components/ui/input"
const schema = z.object({
email: z.email("Enter a valid email address."),
name: z.string().min(2, "Use at least 2 characters."),
})
export function SignupForm() {
const form = useForm<z.infer<typeof schema>>({
resolver: zodResolver(schema),
defaultValues: { email: "", name: "" },
})
return (
<form onSubmit={form.handleSubmit((values) => console.log(values))}>
<FieldGroup>
<Controller
name="name"
control={form.control}
render={({ field, fieldState }) => (
<Field data-invalid={fieldState.invalid}>
<FieldLabel htmlFor="name">Name</FieldLabel>
<Input id="name" aria-invalid={fieldState.invalid} {...field} />
<FieldError errors={[fieldState.error]} />
</Field>
)}
/>
<Controller
name="email"
control={form.control}
render={({ field, fieldState }) => (
<Field data-invalid={fieldState.invalid}>
<FieldLabel htmlFor="email">Email</FieldLabel>
<Input
id="email"
type="email"
autoComplete="email"
aria-invalid={fieldState.invalid}
aria-describedby="email-help"
{...field}
/>
<FieldDescription id="email-help">
We only use it to send your receipt.
</FieldDescription>
<FieldError errors={[fieldState.error]} />
</Field>
)}
/>
<Button type="submit" disabled={form.formState.isSubmitting}>
Create account
</Button>
</FieldGroup>
</form>
)
}FieldError renders with role="alert", so a new error is announced as soon
as it appears. aria-invalid marks the input for screen readers and drives
the error styling.
The older Form, FormField and FormMessage components (from
components/ui/form) are also available if your project already uses them.
These components drop into a Field the same way:
htmlFor and id.type and autoComplete so phones show the right keyboard
and password managers fill it.shouldFocusError).