React Hook Form
Connect Vortex form fields to React Hook Form for form state and validation.
bun add react-hook-formConnect a field
Vortex fields call onChange with an object, such as { name, value, event }, instead of an event. Wrap each field in Controller and pass the value to field.onChange:
SignupForm.tsx
"use client";
import { Button, Stack, TextField } from "@vortexlabs/vortex";
import { Controller, useForm } from "react-hook-form";
type FormValues = {
email: string;
};
const SignupForm = () => {
const { control, handleSubmit } = useForm<FormValues>({
defaultValues: { email: "" },
});
const onSubmit = (values: FormValues) => {
console.log(values);
};
return (
<form noValidate onSubmit={handleSubmit(onSubmit)}>
<Stack gap={4}>
<Controller
name="email"
control={control}
rules={{ required: "Enter your email." }}
render={({ field, fieldState }) => (
<TextField.Root error={fieldState.invalid}>
<TextField.Label>Email</TextField.Label>
<TextField.Input
ref={field.ref}
name={field.name}
value={field.value}
onChange={({ value }) => field.onChange(value)}
onBlur={field.onBlur}
attributes={{ type: "email" }}
/>
<TextField.ErrorMessage>{fieldState.error?.message}</TextField.ErrorMessage>
</TextField.Root>
)}
/>
<Button type="submit" variant="primary">
Sign up
</Button>
</Stack>
</form>
);
};
export default SignupForm;fieldState.invalidsets the root'serror, which showsTextField.ErrorMessage.field.reflets React Hook Form focus the first invalid field on submit.TextAreaconnects the same way.
Other fields
Pass checked for Checkbox and Switch:
<Controller
name="terms"
control={control}
render={({ field, fieldState }) => (
<Checkbox
ref={field.ref}
name={field.name}
label="I accept the terms"
helperText={fieldState.error?.message}
error={fieldState.invalid}
checked={field.value}
onChange={({ checked }) => field.onChange(checked)}
/>
)}
/>Select reports the selected value through onValueChange:
<Controller
name="role"
control={control}
render={({ field, fieldState }) => (
<Select.Root error={fieldState.invalid}>
<Select.Label>Role</Select.Label>
<Select.Field
value={field.value}
onValueChange={(value) => field.onChange(value ?? "")}
onClose={field.onBlur}
loadOptions={loadRoles}
placeholder="Choose a role"
/>
<Select.ErrorMessage>{fieldState.error?.message}</Select.ErrorMessage>
</Select.Root>
)}
/>Schema validation
To validate with a schema instead of rules, add a resolver. This example uses Zod:
bun add zod @hookform/resolversimport { zodResolver } from "@hookform/resolvers/zod";
import { useForm } from "react-hook-form";
import { z } from "zod";
const schema = z.object({
email: z.email("Enter a valid email."),
message: z.string().min(10, "Write at least 10 characters."),
});
type FormValues = z.infer<typeof schema>;
const { control, handleSubmit } = useForm<FormValues>({
resolver: zodResolver(schema),
defaultValues: { email: "", message: "" },
});Field errors come from the schema messages, so the fields above stay the same.