HapplyUI

Components

Form Field

A smart form field wrapper with built-in label, hint, error, and react-hook-form integration. Pass a `name` prop inside a FormProvider to auto-resolve errors — or use manual props for full control.


Installation

bunx @happlyui/cli@latest add form-field

Dependencies

npm packages

  • react-hook-form

Registry dependencies

These are automatically installed when you add this component.

  • happly-ui-utils
  • form-field-context
  • use-form-field-binding
  • label
  • hint

Usage

import * as FormField from "@/components/ui/form-field"

// Automatic mode (with FormProvider)
<FormField.Root name="email" label="Email Address" required hint="This is a hint text.">
  <Input.Composed placeholder="hello@example.com" {...register('email')} />
</FormField.Root>

// Manual mode (without FormProvider)
<FormField.Root label="Email Address" error={error} hint="This is a hint text.">
  <Input.Composed placeholder="hello@example.com" />
</FormField.Root>

Examples

Demo

Props-driven form fields with label, hint, and various input types.

This is a hint text to help user.
<FormField.Root
  label="Email Address"
  htmlFor="email"
  required
  labelSub="(Required)"
  labelInfo="We'll use this to contact you."
  hint="This is a hint text to help user."
>
  <Input.Composed id="email" leadingIcon={RiMailLine} placeholder="hello@example.com" />
</FormField.Root>

With Error

Form field with error state. Error replaces hint and auto-sets hasError.

<FormField.Root
  label="Email Address"
  htmlFor="email"
  required
  hint="This is a hint text."
  error="Please enter a valid email address."
>
  <Input.Composed id="email" leadingIcon={RiMailLine} />
</FormField.Root>

Disabled

Disabled form field. Disabled state auto-flows to children, label, and hint.

This is a hint text to help user.
<FormField.Root
  label="Email Address"
  htmlFor="email"
  required
  hint="This is a hint text."
  disabled
>
  <Input.Composed id="email" leadingIcon={RiMailLine} />
</FormField.Root>

Compound Mode

Full control using sub-components. FormField.Label and FormField.Hint auto-read hasError/disabled from context.

<FormField.Root htmlFor="email" hasError>
  <FormField.Label required sub="(Required)">
    Email Address
  </FormField.Label>
  <Input.Root hasError>
    <Input.Wrapper>
      <Input.Icon as={RiMailLine} />
      <Input.Input id="email" placeholder="hello@example.com" />
    </Input.Wrapper>
  </Input.Root>
  <FormField.Error>Please enter a valid email address.</FormField.Error>
</FormField.Root>

Form Validation

Complete sign-up form with automatic react-hook-form integration. Wrap with FormProvider and pass `name` — errors, hasError, disabled, and id are resolved automatically.

Your first and last name.
We'll never share your email.
Minimum 8 characters.
Tell us a bit about yourself.
const methods = useForm({ resolver: zodResolver(schema), mode: 'onTouched' });

<FormProvider {...methods}>
  <form onSubmit={methods.handleSubmit(onSubmit)}>
    <FormField.Root name="email" label="Email" required>
      <Input.Composed
        leadingIcon={RiMailLine}
        placeholder="hello@example.com"
        {...methods.register('email')}
      />
    </FormField.Root>
  </form>
</FormProvider>

API Reference

FormField.Root

The root form field container. Auto-resolves errors from react-hook-form when `name` is provided inside a FormProvider. Auto-injects `hasError`, `disabled`, and `id` into direct children.

PropTypeDefaultDescription
namestring-Field name for react-hook-form integration. When inside a FormProvider, auto-reads errors from form state. Also used as the default `htmlFor`/`id`.
labelReact.ReactNode-Label text. When provided, renders a composed Label automatically.
htmlForstring-Links label to input via htmlFor/id. Defaults to `name` when not provided.
requiredbooleanfalseShow asterisk on the label
labelSubReact.ReactNode-Sub-text rendered after the label (e.g. '(Optional)')
labelInfoReact.ReactNode-Info tooltip content rendered as Label.Info
hintReact.ReactNode-Hint text rendered below children. Hidden when error is present.
errorReact.ReactNode-Error message. Overrides auto-resolved RHF error when provided.
hasErrorbooleanfalseError state. Auto-set when error is present (manual or RHF-resolved).
disabledbooleanfalseDisabled state. Auto-injected into direct children, label, and hint.
classNamestring-Additional className to override or extend styling

FormField.Label

Composed label that auto-reads disabled and htmlFor from FormField context.

PropTypeDefaultDescription
requiredbooleanfalseShow asterisk
subReact.ReactNode-Sub-text after label
infoReact.ReactNode-Info tooltip content

FormField.Hint

Composed hint that auto-reads hasError and disabled from FormField context.

PropTypeDefaultDescription
iconReact.ElementType-Custom icon component. Defaults to built-in info circle.

FormField.Error

Error message display with error styling. Returns null when children is falsy.

PropTypeDefaultDescription

useFormField

Hook to access FormField context (hasError, disabled, id, name) from custom child components.

PropTypeDefaultDescription

Previous
Digit Input
Next
Hint