React Hook Form
How to use React Hook Form with the React components of the NayaOne Design System.
register() works for components that forward a ref to a native input.
Select, SelectInput, and other select-like components
keep their own value, so they need a controlled value / onValueChange pair.
Installation
Section titled “Installation”Using Bun
Section titled “Using Bun”bun install react-hook-formUsing npm
Section titled “Using npm”npm install react-hook-formRegister a native input
Section titled “Register a native input”TextInput, TextAreaInput, Password, PasswordInput,
and the native controls on Field (Field.Input, Field.Textarea, Field.Select) forward ref to a real element.
Spread the object returned by register() onto the component, and pass invalid and error from formState to show a validation message.
Set noValidate on the form so React Hook Form runs the rules.
import { useForm } from 'react-hook-form';import { TextAreaInput, TextInput,} from '@fintech-sandpit/ui/react';
type ProfileFormValues = { name: string; bio: string;};
const ProfileForm = () => { const { register, handleSubmit, formState: { errors }, } = useForm<ProfileFormValues>();
const onSubmit = (values: ProfileFormValues) => { // send values };
return ( <form noValidate onSubmit={handleSubmit(onSubmit)} > <TextInput {...register('name', { required: 'Name is required' })} label="Name" placeholder="Jane Doe" invalid={Boolean(errors.name)} error={errors.name?.message} required />
<TextAreaInput {...register('bio')} label="Bio" placeholder="A short bio" rows={3} invalid={Boolean(errors.bio)} error={errors.bio?.message} /> </form> );};Control a Select
Section titled “Control a Select”Select and SelectInput read and write their value
through value and onValueChange. Connect that pair with Controller.
Give the field a defaultValues entry so the select stays controlled. With multiple,
both value and the onValueChange argument are string[].
The same value / onValueChange pair applies to Combobox, ListBox,
NumberInput, DatePicker, ColorInput,
TagInput, Switch, Radio, and RadioCard.
Checkbox is controlled with checked and onCheckedChange.
import { Controller, useForm,} from 'react-hook-form';import { createListCollection, SelectInput, type ListCollectionItem,} from '@fintech-sandpit/ui/react';
type PetFormValues = { pet: string;};
const pets = createListCollection<ListCollectionItem>({ items: [ { label: 'Dog', value: 'dog' }, { label: 'Cat', value: 'cat' }, { label: 'Parrot', value: 'parrot' }, ],});
const PetForm = () => { const { control, handleSubmit, formState: { errors }, } = useForm<PetFormValues>({ defaultValues: { pet: '', }, });
const onSubmit = (values: PetFormValues) => { // send values };
return ( <form noValidate onSubmit={handleSubmit(onSubmit)} > <Controller name="pet" control={control} rules={{ required: 'Please select a pet' }} render={({ field }) => ( <SelectInput name={field.name} collection={pets} label="Pet" placeholder="Select a pet" value={field.value} invalid={Boolean(errors.pet)} error={errors.pet?.message} required onBlur={field.onBlur} onValueChange={value => field.onChange(value ?? '')} /> )} /> </form> );};Split a form across components
Section titled “Split a form across components”Call useForm once in the parent and pass the return value to FormProvider.
Child components read the same form with useFormContext.
Native inputs still use register().
Select-like inputs still use Controller with value and onValueChange.
import { Controller, FormProvider, useForm, useFormContext,} from 'react-hook-form';import { createListCollection, SelectInput, TextInput, type ListCollectionItem,} from '@fintech-sandpit/ui/react';
type SignupFormValues = { name: string; role: string;};
const roles = createListCollection<ListCollectionItem>({ items: [ { label: 'Admin', value: 'admin' }, { label: 'Member', value: 'member' }, ],});
const NameField = () => { const { register, formState: { errors }, } = useFormContext<SignupFormValues>();
return ( <TextInput {...register('name', { required: 'Name is required' })} label="Name" placeholder="Jane Doe" invalid={Boolean(errors.name)} error={errors.name?.message} required /> );};
const RoleField = () => { const { control, formState: { errors }, } = useFormContext<SignupFormValues>();
return ( <Controller name="role" control={control} rules={{ required: 'Please select a role' }} render={({ field }) => ( <SelectInput name={field.name} collection={roles} label="Role" placeholder="Select a role" value={field.value} invalid={Boolean(errors.role)} error={errors.role?.message} required onBlur={field.onBlur} onValueChange={value => field.onChange(value ?? '')} /> )} /> );};
const SignupForm = () => { const form = useForm<SignupFormValues>({ defaultValues: { name: '', role: '', }, });
const onSubmit = (values: SignupFormValues) => { // send values };
return ( <FormProvider {...form}> <form noValidate onSubmit={form.handleSubmit(onSubmit)} > <NameField /> <RoleField /> </form> </FormProvider> );};