shadcn.io is an independent project built on shadcn/ui
Three plans sit under Select your plan: Free at $0 a month, Pro at $9 a month, and Enterprise. Nothing starts selected. Continue shows You need to select a plan until one is picked.
Use it when the choices are few and only one can win. A long list people search is a Combobox. In React, Ark UI keeps this as one radio group.
Installation
Usage
<form className="w-full max-w-md" onSubmit={form.handleSubmit(onSubmit)}>
<FieldGroup>
<Controller
control={form.control}
name="plan"
render={({ field, fieldState }) => (
<Field invalid={fieldState.invalid}>
<FieldSet>
<FieldLegend variant="label">Select your plan</FieldLegend>
<RadioGroup
name={field.name}
onValueChange={({ value }) => field.onChange(value)}
value={field.value ?? null}
>
{plans.map((plan) => (
<Field
invalid={fieldState.invalid}
key={plan.value}
orientation="horizontal"
>
<RadioGroupItem value={plan.value}>
{plan.label}
</RadioGroupItem>
</Field>
))}
</RadioGroup>
<FieldDescription>
Choose the plan that best fits your needs.
</FieldDescription>
<FieldError>{fieldState.error?.message}</FieldError>
</FieldSet>
{/* The rest of the file is under Installation. */}FAQ
What values can the plan field take?
plan is z.enum(["free", "pro", "enterprise"]). The labels are "Free - $0/month", "Pro - $9/month" and "Enterprise - Contact us", and the form stores the enum value, such as pro.
How does a choice get into React Hook Form?
A Controller named plan renders the group, and each option is a horizontal Field. RadioGroup takes value={field.value ?? null} and onValueChange passes value to field.onChange; each RadioGroupItem holds its label.
What error shows if nothing is selected?
The enum's error is "You need to select a plan." Continue with no choice marks the Field invalid and shows it in the FieldError under the description.
Is a plan selected on first paint?
No. useForm gets only the resolver, so plan starts unset and no option is checked. The group's legend is "Select your plan".
API Reference
Field
Sets invalid={fieldState.invalid}, variant="label" and orientation="horizontal". The rest is on the Field page.
Radio Group
Sets name={field.name}, onValueChange, value and value={plan.value}. The rest is on the Radio Group page.
Button
Sets type="submit". The rest is on the Button page.





