Form
optics/formFreeComponent
Builds forms with React Hook Form.
See it running
Open the demo on optics
optics.agusmayol.com.ar/components/formInstall it
npx shadcn@latest add https://optics.agusmayol.com.ar/r/form.jsonSource
1"use client";2import * as React from "react";3import {4 Controller,5 FormProvider,6 useFormContext,7 useFormState,8} from "react-hook-form";910import { cn } from "@/registry/optics/lib/utils";11import { Label } from "@/registry/optics/label";1213const Slot = ({ children, ...props }) => {14 if (React.isValidElement(children)) {15 return React.cloneElement(children, {16 ...props,17 ...children.props,18 className: cn(props.className, children.props.className),19 });20 }21 return null;22};2324const Form = FormProvider;2526const FormFieldContext = React.createContext({ name: "" });2728const FormField = ({ ...props }) => {29 return (30 <FormFieldContext.Provider value={{ name: props.name }}>31 <Controller {...props} />32 </FormFieldContext.Provider>33 );34};3536const useFormField = () => {37 const fieldContext = React.useContext(FormFieldContext);38 const itemContext = React.useContext(FormItemContext);39 const { getFieldState } = useFormContext();40 const formState = useFormState({ name: fieldContext.name });41 const fieldState = getFieldState(fieldContext.name, formState);4243 if (!fieldContext) {44 throw new Error("useFormField should be used within <FormField>");45 }4647 const { id } = itemContext;4849 return {50 id,51 name: fieldContext.name,52 formItemId: `${id}-form-item`,53 formDescriptionId: `${id}-form-item-description`,54 formMessageId: `${id}-form-item-message`,55 ...fieldState,56 };57};5859const FormItemContext = React.createContext({});6061function FormItem({ className = "", ...props }) {62 const id = React.useId();6364 return (65 <FormItemContext.Provider value={{ id }}>66 <div67 data-slot="form-item"68 className={cn("grid gap-2", className)}69 {...props}70 />71 </FormItemContext.Provider>72 );73}7475function FormLabel({ className = "", ...props }) {76 const { error, formItemId } = useFormField();7778 return (79 <Label80 data-slot="form-label"81 data-error={!!error}82 className={cn("data-[error=true]:text-destructive", className)}83 htmlFor={formItemId}84 {...props}85 />86 );87}8889function FormControl({ render = undefined, ...props }) {90 const { error, formItemId, formDescriptionId, formMessageId } =91 useFormField();9293 return (94 <Slot95 data-slot="form-control"96 id={formItemId}97 aria-describedby={98 !error99 ? `${formDescriptionId}`100 : `${formDescriptionId} ${formMessageId}`101 }102 aria-invalid={!!error}103 {...props}104 />105 );106}107108function FormDescription({ className = "", ...props }) {109 const { formDescriptionId } = useFormField();110111 return (112 <p113// … truncated
What it pulls in
npm packages
Other registry items
Files it writes
More from optics
All 79 items| Component | Registry | Kind | Access | Installs | Command |
|---|---|---|---|---|---|
| Accordionaccordion | optics | Components | Free | 5 deps | |
| Alertalert | optics | Components | Free | 4 deps | |
| Alert Dialogalert-dialog | optics | Components | Free | 4 deps | |
| Aspect Ratioaspect-ratio | optics | Components | Free | 2 deps | |
| Auto Heightauto-height | optics | Components | Free | 3 deps | |
| Avataravatar | optics | Components | Free | 3 deps | |
| Badgebadge | optics | Components | Free | 4 deps | |
| Breadcrumbbreadcrumb | optics | Components | Free | 4 deps |
