RHFField
React Hook Form adapter that wires useController through Cynosure's FormField scaffolding — label, error, aria-invalid, and message in one component.
Inherits the underlying control's a11y; surfaces validation errors via aria-describedby and aria-invalid.
import { Input } from '@arshad-shah/cynosure-react';
import { RHFField } from '@arshad-shah/cynosure-react/rhf';
import { useForm } from 'react-hook-form';
type Values = { email: string };
export default function Example() {
const { control, handleSubmit } = useForm<Values>({ defaultValues: { email: '' } });
return (
<form
onSubmit={handleSubmit(() => {
// submit handler — wire your network call here
})}
style={{ display: 'flex', flexDirection: 'column', gap: '0.75rem', width: '320px' }}
>
<RHFField control={control} name="email" label="Email">
<Input type="email" placeholder="you@example.com" />
</RHFField>
</form>
);
}
RHFField accepts the control from useForm() plus a name and label, and clones its single child to inject value, onChange, onBlur, and ref from useController.
import { useForm } from "react-hook-form";import { RHFField, Input } from "@arshad-shah/cynosure-react/rhf";
function MyForm() { const { control, handleSubmit } = useForm({ defaultValues: { email: "" } });
return ( <form onSubmit={handleSubmit(console.log)}> <RHFField control={control} name="email" label="Email"> <Input type="email" /> </RHFField> </form> );}react-hook-form is declared as an optional peer dependency — consumers that never import RHFField don’t need it installed.
With validation
Section titled “With validation”Pass rules (RHF’s RegisterOptions) to validate the field; the error message is announced via FormMessage.
import { Button, Input } from '@arshad-shah/cynosure-react';
import { RHFField } from '@arshad-shah/cynosure-react/rhf';
import { useForm } from 'react-hook-form';
type Values = { username: string };
export default function Example() {
const { control, handleSubmit } = useForm<Values>({
defaultValues: { username: '' },
mode: 'onBlur',
});
return (
<form
onSubmit={handleSubmit(() => {
// submit handler — validate happens before this fires
})}
style={{ display: 'flex', flexDirection: 'column', gap: '0.75rem', width: '320px' }}
>
<RHFField
control={control}
name="username"
label="Username"
required
rules={{
required: 'Username is required',
minLength: { value: 3, message: 'At least 3 characters' },
}}
>
<Input placeholder="ada-lovelace" />
</RHFField>
<Button type="submit">Save</Button>
</form>
);
}
With description
Section titled “With description”Supply description to render helper copy below the control.
import { Input } from '@arshad-shah/cynosure-react';
import { RHFField } from '@arshad-shah/cynosure-react/rhf';
import { useForm } from 'react-hook-form';
type Values = { handle: string };
export default function Example() {
const { control } = useForm<Values>({ defaultValues: { handle: '' } });
return (
<div style={{ width: '320px' }}>
<RHFField
control={control}
name="handle"
label="Handle"
description="Letters, numbers, and underscores only."
>
<Input placeholder="@arshad" />
</RHFField>
</div>
);
}
Required and disabled
Section titled “Required and disabled”The required prop marks the Label and propagates to the inner control; disabled is forwarded as well.
import { Input } from '@arshad-shah/cynosure-react';
import { RHFField } from '@arshad-shah/cynosure-react/rhf';
import { useForm } from 'react-hook-form';
type Values = { name: string; id: string };
export default function Example() {
const { control } = useForm<Values>({ defaultValues: { name: '', id: 'usr_9f2c4' } });
return (
<div style={{ display: 'flex', flexDirection: 'column', gap: '1rem', width: '320px' }}>
<RHFField control={control} name="name" label="Display name" required>
<Input placeholder="Required field" />
</RHFField>
<RHFField control={control} name="id" label="Account ID" disabled>
<Input />
</RHFField>
</div>
);
}
Accessibility
Section titled “Accessibility”- The
labelprop renders insideFormLabel, wired byhtmlForto the cloned input child. - Validation errors are announced via
FormMessageand linked througharia-describedbyandaria-invalidon the input. - The single child must forward
ref,value, andonChange— all native and Cynosure inputs do.
Recipes
Section titled “Recipes”- For non-input controls (Slider, RadioGroup, Select), wrap with the same pattern —
RHFFieldinjectsvalue/onChange/onBlur/ref, which they all consume. - Group multiple
RHFFields under a singleuseFormand submit viahandleSubmit. - Pass
rules={{ required: "…" }}to drive both required marking and inline messaging in one place.