Form
rivelle/formFreeComponent
Accessible react-hook-form bindings for labels, controls and validation messages.
Live preview
live · rendered by the registry
Rendered by rivelle on their own page — this is the component running, not a screenshot of it.Install it
npx shadcn@latest add https://rivelle.dev/r/form.jsonSource
1"use client";23import * as React from "react";4import * as LabelPrimitive from "@radix-ui/react-label";5import { Slot } from "@radix-ui/react-slot";6import {7 Controller,8 FormProvider,9 useFormContext,10 type ControllerProps,11 type FieldPath,12 type FieldValues,13} from "react-hook-form";1415import { cn } from "@/lib/utils";16import { Label } from "@/components/ui/label";1718const Form = FormProvider;1920type FormFieldContextValue<21 TFieldValues extends FieldValues = FieldValues,22 TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>,23> = { name: TName };24const FormFieldContext = React.createContext<FormFieldContextValue>(25 {} as FormFieldContextValue,26);2728function FormField<29 TFieldValues extends FieldValues = FieldValues,30 TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>,31>(props: ControllerProps<TFieldValues, TName>) {32 return (33 <FormFieldContext.Provider value={{ name: props.name }}>34 <Controller {...props} />35 </FormFieldContext.Provider>36 );37}3839const FormItemContext = React.createContext<{ id: string }>({ id: "" });4041function FormItem({ className, ...props }: React.ComponentProps<"div">) {42 const id = React.useId();43 return (44 <FormItemContext.Provider value={{ id }}>45 <div46 data-slot="form-item"47 className={cn("grid gap-2", className)}48 {...props}49 />50 </FormItemContext.Provider>51 );52}5354function useFormField() {55 const fieldContext = React.useContext(FormFieldContext);56 const itemContext = React.useContext(FormItemContext);57 const { getFieldState, formState } = useFormContext();58 if (!fieldContext.name)59 throw new Error("useFormField must be used within <FormField>");60 const fieldState = getFieldState(fieldContext.name, formState);61 return {62 id: itemContext.id,63 name: fieldContext.name,64 formItemId: `${itemContext.id}-form-item`,65 formDescriptionId: `${itemContext.id}-form-item-description`,66 formMessageId: `${itemContext.id}-form-item-message`,67 ...fieldState,68 };69}7071function FormLabel({72 className,73 ...props74}: React.ComponentProps<typeof LabelPrimitive.Root>) {75 const { error, formItemId } = useFormField();76 return (77 <Label78 data-slot="form-label"79 data-error={!!error}80 className={cn("data-[error=true]:text-destructive", className)}81 htmlFor={formItemId}82 {...props}83 />84 );85}8687function FormControl(props: React.ComponentProps<typeof Slot>) {88// … truncated
What it pulls in
npm packages
Other registry items
Files it writes
More from rivelle
All 83 items| Component | Registry | Kind | Access | Installs | Command |
|---|---|---|---|---|---|
| Accordionaccordion | rivelle | Components | Free | 2 deps | |
| Alertalert | rivelle | Components | Free | 1 dep | |
| Alert Dialogalert-dialog | rivelle | Components | Free | 1 dep | |
| Aspect Ratioaspect-ratio | rivelle | Components | Free | 1 dep | |
| Avataravatar | rivelle | Components | Free | 1 dep | |
| Badgebadge | rivelle | Components | Free | 2 deps | |
| Breadcrumbbreadcrumb | rivelle | Components | Free | 2 deps | |
| Buttonbutton | rivelle | Components | Free | 2 deps |
