Registry

Command Palette

Search for a command to run...

Login
registry:component

Form

v1.0.0ga

React Hook Form helpers for typed fields, labels, and validation messaging.

Registry Endpoint
Open JSON

npx shadcn@latest add @aavya/ui-form

One-time setup — add the @aavya registry to your components.json so branded dependencies resolve to Aavya (not stock shadcn):

"registries": { "@aavya": "https://registry-dev.aavya.com/r/{name}.json" }

Or install by full URL: npx shadcn@latest add https://registry-dev.aavya.com/r/ui-form.json

Live Preview

We use this for account updates.

Guidelines

Do

  • Associate every control with a label using the Form primitives (FormField / FormLabel).
  • Show inline, specific error messages via FormMessage and link them to their field.
  • Group related fields and provide a single clear submit action.

Don’t

  • Don't validate only on submit for long forms — give field-level feedback.
  • Don't disable the submit button without explaining what's missing.
  • Don't convey errors with color alone.
Accessibility

Role

Native form controls with label / description / error wiring (handled by the Form primitives).

Keyboard

Standard field navigation and submit. (handled)

Screen reader

Labels, descriptions, and errors are associated via aria-describedby / aria-invalid when you use FormField / FormLabel / FormMessage. You provide the content.

Focus

On an invalid submit, move focus to the first errored field — you provide this.

Gotcha

Errors must be programmatically linked (FormMessage) — a visual-only error near a field isn't announced.

Dependencies

npm

react-hook-form
Usage Snippet
import { Form } from '@/components/ui/form'

export default function Example() {
  return (
    <div className="p-6">
      <Form />
    </div>
  )
}
Source Preview
'use client'

import * as React from 'react'

import type * as LabelPrimitive from '@radix-ui/react-label'
import { Slot } from '@radix-ui/react-slot'
import {
  Controller,
  FormProvider,
  useFormContext,
  useFormState,
  type ControllerProps,
  type FieldPath,
  type FieldValues
} from 'react-hook-form'

import { cn } from '@/lib/utils'
import { Label } from '@/components/ui/label'

const Form = FormProvider

type FormFieldContextValue<
  TFieldValues extends FieldValues = FieldValues,
  TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>
> = {
  name: TName
}

const FormFieldContext = React.createContext<FormFieldContextValue>({} as FormFieldContextValue)

const FormField = <
  TFieldValues extends FieldValues = FieldValues,
  TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>
>({
  ...props
}: ControllerProps<TFieldValues, TName>) => {
  return (
    <FormFieldContext.Provider value={{ name: props.name }}>
      <Controller {...props} />
    </FormFieldContext.Provider>
  )
}

const useFormField = () => {
  const fieldContext = React.useContext(FormFieldContext)
  const itemContext = React.useContext(FormItemContext)
  const { getFieldState } = useFormContext()
  const formState = useFormState({ name: fieldContext.name })
  const fieldState = getFieldState(fieldContext.name, formState)

  if (!fieldContext) {
    throw new Error('useFormField sho