Efniv0.1

Components

Form

react-hook-form + zod. Wraps Controller, wires labels to inputs, and surfaces validation messages using semantic destructive tokens.

When to usefor any form with more than one field where validation, focus management, or async submission matters.

Import

import {
  Form, FormField, FormItem, FormLabel,
  FormControl, FormDescription, FormMessage,
} from "@ravnhq/efni/form"
import { useForm } from "react-hook-form"
import { zodResolver } from "@hookform/resolvers/zod"
import { z } from "zod"

New mission

Short, unique — this shows up in the ops table.

const schema = z.object({
  name: z.string().min(3),
  operator: z.string().min(1),
  scheduled: z.string().min(1),
})

const form = useForm<z.infer<typeof schema>>({
  resolver: zodResolver(schema),
  defaultValues: { name: "", operator: "", scheduled: "" },
})

<Form {...form}>
  <form onSubmit={form.handleSubmit(onSubmit)}>
    <FormField
      control={form.control}
      name="name"
      render={({ field }) => (
        <FormItem>
          <FormLabel>Mission name</FormLabel>
          <FormControl><Input {...field} /></FormControl>
          <FormMessage />
        </FormItem>
      )}
    />
    <Button type="submit">Create</Button>
  </form>
</Form>

Accessibility

KeyAction
TabMove through fields in DOM order.
EnterSubmit the form when focused inside a text input.
  • FormControl clones its child to inject id, aria-describedby, and aria-invalid — you don't manage them yourself.
  • FormMessage renders nothing when there's no error, so the field height doesn't jump.
  • Every input should have a FormLabel — never skip it in favour of a placeholder.

Guidelines

Do
  • Keep the field label above the input; helper text below in FormDescription.
  • Validate on submit for short forms; on blur for longer ones.
  • Reset the form after a successful submit; don't clear it silently.
Don't
  • Don't nest a FormField in a FormField — one Controller per field.
  • Don't use placeholder text as the label — screen readers skip it and translation breaks.
  • Don't gate submit behind form.formState.isValid alone — server errors won't be represented.

Props

PropTypeDefaultDescription
FormFormProviderProps—Re-export of react-hook-form FormProvider.
FormFieldControllerProps—Wraps <Controller>; must be inside <Form>.
FormItemHTMLAttributes<HTMLDivElement>—Container that provides the id context for label + control.
FormLabelReact.ComponentProps<typeof Label>—Label wired to the item's id; goes red on error.
FormControl{ children: ReactElement }—Clones its child to inject id + aria-* attributes.
FormDescriptionHTMLAttributes<HTMLParagraphElement>—Helper text, wired via aria-describedby.
FormMessageHTMLAttributes<HTMLParagraphElement>—Shows the field's error message, or nothing when valid.