Components
Forms & Input
Form

Form

Use Form to build consistent, accessible interfaces aligned with the shared design system.

Installation

1. Copy the source code:

  • Source in this monorepo: packages/ui/src/components/form.tsx
  • Place in your project: components/ui/form.tsx
  • Required utility: lib/utils.ts (for cn helper)

2. Update the import paths to match your project structure.


Usage

import { useForm } from "react-hook-form";
import { Button } from "@hoag/ui/components/button";
import {
  Form,
  FormControl,
  FormDescription,
  FormField,
  FormItem,
  FormLabel,
  FormMessage,
} from "@hoag/ui/components/form";
import { Input } from "@hoag/ui/input";
 
export default function Demo() {
  const form = useForm({
    defaultValues: {
      email: "",
    },
  });
 
  return (
    <Form {...form}>
      <form onSubmit={form.handleSubmit(() => {})} className="space-y-4">
        <FormField
          control={form.control}
          name="email"
          rules={{ required: "Email is required." }}
          render={({ field }) => (
            <FormItem>
              <FormLabel>Email</FormLabel>
              <FormControl>
                <Input type="email" placeholder="name@example.com" {...field} />
              </FormControl>
              <FormDescription>
                We will use this for account updates.
              </FormDescription>
              <FormMessage />
            </FormItem>
          )}
        />
        <Button type="submit">Submit</Button>
      </form>
    </Form>
  );
}

Import

import {
  Form,
  FormControl,
  FormDescription,
  FormField,
  FormItem,
  FormLabel,
  FormMessage,
  useFormField,
} from "@hoag/ui/components/form";

Basic Example

We will use this for account updates.

import { useForm } from "react-hook-form";
import { Button } from "@hoag/ui/components/button";
import {
  Form,
  FormControl,
  FormDescription,
  FormField,
  FormItem,
  FormLabel,
  FormMessage,
} from "@hoag/ui/components/form";
import { Input } from "@hoag/ui/input";
 
export function FormBasicExample() {
  const form = useForm({
    defaultValues: {
      email: "",
    },
  });
 
  return (
    <Form {...form}>
      <form onSubmit={form.handleSubmit(() => {})} className="space-y-4">
        <FormField
          control={form.control}
          name="email"
          rules={{ required: "Email is required." }}
          render={({ field }) => (
            <FormItem>
              <FormLabel>Email</FormLabel>
              <FormControl>
                <Input type="email" placeholder="name@example.com" {...field} />
              </FormControl>
              <FormDescription>
                We will use this for account updates.
              </FormDescription>
              <FormMessage />
            </FormItem>
          )}
        />
        <Button type="submit">Submit</Button>
      </form>
    </Form>
  );
}

Accessibility Checklist

  • Verify semantic roles and ARIA attributes for interactive behavior.
  • Keep keyboard navigation and focus visibility consistent in all states.
  • Ensure color contrast meets accessibility requirements in light and dark themes.
  • Provide screen reader context for dynamic state or content changes.

Testing Checklist

  • Add render tests for default and key variant states.
  • Add interaction tests for callbacks and state transitions.
  • Add visual regression checks for responsive and theme variations.
  • Cover edge states such as loading, empty, disabled, and error.

Production Tips

  • Wrap component logic in feature-level abstractions for domain behavior.
  • Keep visual variants centralized to avoid style drift across screens.
  • Reuse a single import path strategy for simpler refactors.
  • Mirror documented states in Storybook and QA checklists.

Named Exports

  • Form
  • FormControl
  • FormDescription
  • FormField
  • FormItem
  • FormLabel
  • FormMessage
  • useFormField

API Reference

For authoritative prop signatures and implementation details, inspect:

  • packages/ui/src/components/form.tsx

MIT 2026 © @hoag/ui