{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "form-layouts-01",
  "type": "registry:component",
  "title": "Form Layouts",
  "author": "Toby Belhome",
  "description": "Design clean and efficient dashboard forms with Tailwind CSS form layouts. These sections feature organized fields, clear labels, and responsive design to enhance usability and data entry. Perfect for admin panels, SaaS platforms, and analytics dashboards aiming for modern, user-friendly, and accessible forms.",
  "dependencies": [
    "@hookform/resolvers",
    "react-hook-form",
    "zod",
    "lucide-react"
  ],
  "registryDependencies": [
    "button",
    "input",
    "textarea",
    "avatar",
    "checkbox",
    "radio-group",
    "label",
    "form"
  ],
  "files": [
    {
      "path": "examples/blocks/dashboard-ui/form-layouts/01/page.tsx",
      "content": "\"use client\";\n\nimport { zodResolver } from \"@hookform/resolvers/zod\";\nimport { useForm } from \"react-hook-form\";\nimport * as z from \"zod\";\nimport { Button } from \"@/components/ui/button\";\nimport { Input } from \"@/components/ui/input\";\nimport { Textarea } from \"@/components/ui/textarea\";\nimport { Avatar, AvatarFallback, AvatarImage } from \"@/components/ui/avatar\";\nimport { Checkbox } from \"@/components/ui/checkbox\";\nimport { RadioGroup, RadioGroupItem } from \"@/components/ui/radio-group\";\nimport {\n  InputGroup,\n  InputGroupAddon,\n  InputGroupInput,\n  InputGroupText\n} from \"@/components/ui/input-group\";\nimport {\n  Select,\n  SelectContent,\n  SelectItem,\n  SelectTrigger,\n  SelectValue\n} from \"@/components/ui/select\";\nimport { Label } from \"@/components/ui/label\";\nimport { AlertCircleIcon, User, XIcon } from \"lucide-react\";\nimport { useFileUpload } from \"@/hooks/use-file-upload\";\nimport { Form } from \"@/components/ui/form\";\n\nconst profileFormSchema = z.object({\n  username: z\n    .string()\n    .min(3, { message: \"Username must be at least 3 characters.\" })\n    .max(30, { message: \"Username must not be longer than 30 characters.\" })\n    .regex(/^[a-zA-Z0-9_-]+$/, {\n      message: \"Username can only contain letters, numbers, underscores, and hyphens.\"\n    }),\n  about: z\n    .string()\n    .max(500, { message: \"About must not be longer than 500 characters.\" })\n    .optional(),\n  firstName: z.string().min(1, { message: \"First name is required.\" }),\n  lastName: z.string().min(1, { message: \"Last name is required.\" }),\n  email: z.email({ message: \"Please enter a valid email address.\" }),\n  country: z.string().min(1, { message: \"Please select a country.\" }),\n  street: z.string().min(1, { message: \"Street address is required.\" }),\n  city: z.string().min(1, { message: \"City is required.\" }),\n  state: z.string().min(1, { message: \"State/Province is required.\" }),\n  zip: z.string().min(1, { message: \"ZIP/Postal code is required.\" }),\n  notifications: z.object({\n    comments: z.boolean(),\n    candidates: z.boolean(),\n    offers: z.boolean()\n  }),\n  pushNotifications: z.enum([\"everything\", \"same-as-email\", \"no-push\"])\n});\n\nexport default function ProfileForm() {\n  const form = useForm<z.infer<typeof profileFormSchema>>({\n    resolver: zodResolver(profileFormSchema),\n    defaultValues: {\n      username: \"\",\n      about: \"\",\n      firstName: \"\",\n      lastName: \"\",\n      email: \"\",\n      country: \"us\",\n      street: \"\",\n      city: \"\",\n      state: \"\",\n      zip: \"\",\n      notifications: {\n        comments: true,\n        candidates: false,\n        offers: false\n      },\n      pushNotifications: \"everything\"\n    }\n  });\n\n  const {\n    register,\n    handleSubmit,\n    watch,\n    setValue,\n    formState: { errors }\n  } = form;\n\n  const watchedValues = watch();\n\n  const maxSizeMB = 5;\n  const maxSize = maxSizeMB * 1024 * 1024; // 5MB default\n  const [\n    { files, isDragging, errors: fileUploadErrors },\n    {\n      handleDragEnter,\n      handleDragLeave,\n      handleDragOver,\n      handleDrop,\n      openFileDialog,\n      removeFile,\n      getInputProps\n    }\n  ] = useFileUpload({\n    accept: \"image/*\",\n    maxSize\n  });\n  const avatarUrl = files[0]?.preview || null;\n\n  function onSubmit(values: z.infer<typeof profileFormSchema>) {\n    console.log(values);\n  }\n\n  return (\n    <div className=\"mx-auto max-w-3xl px-4 py-10 lg:py-20\">\n      <Form {...form}>\n        <form onSubmit={handleSubmit(onSubmit)} className=\"space-y-4 lg:space-y-8\">\n          {/* Profile Section */}\n          <div className=\"space-y-6\">\n            <div>\n              <h2 className=\"text-foreground text-xl font-semibold\">Profile</h2>\n              <p className=\"text-muted-foreground mt-1 text-sm\">\n                This information will be displayed publicly so be careful what you share.\n              </p>\n            </div>\n\n            <div className=\"space-y-6\">\n              {/* Username */}\n              <div className=\"grid gap-2\">\n                <Label htmlFor=\"username\">Username</Label>\n                <InputGroup>\n                  <InputGroupInput\n                    id=\"username\"\n                    placeholder=\"janesmith\"\n                    {...register(\"username\")}\n                    className=\"!pl-1\"\n                  />\n                  <InputGroupAddon>\n                    <InputGroupText>bundui.io/</InputGroupText>\n                  </InputGroupAddon>\n                </InputGroup>\n                {errors.username && (\n                  <p className=\"text-destructive text-sm\">{errors.username.message}</p>\n                )}\n                <p className=\"text-muted-foreground text-sm\">\n                  workcation.com/{watchedValues.username || \"username\"}\n                </p>\n              </div>\n\n              {/* About */}\n              <div className=\"grid gap-2\">\n                <Label htmlFor=\"about\">About</Label>\n                <Textarea id=\"about\" rows={6} className=\"resize-none\" {...register(\"about\")} />\n                {errors.about && <p className=\"text-destructive text-sm\">{errors.about.message}</p>}\n                <p className=\"text-muted-foreground text-sm\">\n                  Write a few sentences about yourself.\n                </p>\n              </div>\n\n              {/* Photo */}\n              <div className=\"space-y-2\">\n                <Label>Photo</Label>\n                <div className=\"flex items-center gap-4\">\n                  <Avatar className=\"size-12 border\">\n                    <AvatarImage src={`${avatarUrl}`} />\n                    <AvatarFallback className=\"bg-muted\">\n                      <User className=\"text-muted-foreground h-6 w-6\" />\n                    </AvatarFallback>\n                  </Avatar>\n                  <div className=\"flex gap-2\">\n                    <Button\n                      type=\"button\"\n                      variant=\"secondary\"\n                      onClick={openFileDialog}\n                      onDragEnter={handleDragEnter}\n                      onDragLeave={handleDragLeave}\n                      onDragOver={handleDragOver}\n                      onDrop={handleDrop}\n                      data-dragging={isDragging || undefined}\n                      aria-label={avatarUrl ? \"Change image\" : \"Upload image\"}>\n                      Change\n                    </Button>\n                    {avatarUrl && (\n                      <Button\n                        type=\"button\"\n                        variant=\"destructive\"\n                        onClick={() => removeFile(files[0]?.id)}\n                        size=\"icon\"\n                        aria-label=\"Remove image\">\n                        <XIcon className=\"size-3.5\" />\n                      </Button>\n                    )}\n                  </div>\n                  <input\n                    {...getInputProps()}\n                    className=\"sr-only\"\n                    aria-label=\"Upload image file\"\n                    tabIndex={-1}\n                  />\n                </div>\n                {fileUploadErrors.length > 0 && (\n                  <div className=\"text-destructive flex items-center gap-1 text-xs\" role=\"alert\">\n                    <AlertCircleIcon className=\"size-3 shrink-0\" />\n                    <span>{fileUploadErrors[0]}</span>\n                  </div>\n                )}\n              </div>\n            </div>\n          </div>\n\n          {/* Personal Information Section */}\n          <div className=\"border-border space-y-6 border-t pt-8\">\n            <div>\n              <h2 className=\"text-foreground text-xl font-semibold\">Personal Information</h2>\n              <p className=\"text-muted-foreground mt-1 text-sm\">\n                Use a permanent address where you can receive mail.\n              </p>\n            </div>\n\n            <div className=\"space-y-6\">\n              {/* First and Last Name */}\n              <div className=\"grid gap-6 sm:grid-cols-2\">\n                <div className=\"grid gap-2\">\n                  <Label htmlFor=\"firstName\">First name</Label>\n                  <Input id=\"firstName\" {...register(\"firstName\")} />\n                  {errors.firstName && (\n                    <p className=\"text-destructive text-sm\">{errors.firstName.message}</p>\n                  )}\n                </div>\n                <div className=\"grid gap-2\">\n                  <Label htmlFor=\"lastName\">Last name</Label>\n                  <Input id=\"lastName\" {...register(\"lastName\")} />\n                  {errors.lastName && (\n                    <p className=\"text-destructive text-sm\">{errors.lastName.message}</p>\n                  )}\n                </div>\n              </div>\n\n              {/* Email */}\n              <div className=\"grid gap-2\">\n                <Label htmlFor=\"email\">Email address</Label>\n                <Input id=\"email\" type=\"email\" className=\"max-w-md\" {...register(\"email\")} />\n                {errors.email && <p className=\"text-destructive text-sm\">{errors.email.message}</p>}\n              </div>\n\n              {/* Country */}\n              <div className=\"grid gap-2\">\n                <Label htmlFor=\"country\">Country</Label>\n                <Select\n                  value={watchedValues.country}\n                  onValueChange={(value) => setValue(\"country\", value)}>\n                  <SelectTrigger id=\"country\" className=\"w-full\">\n                    <SelectValue />\n                  </SelectTrigger>\n                  <SelectContent>\n                    <SelectItem value=\"us\">United States</SelectItem>\n                    <SelectItem value=\"ca\">Canada</SelectItem>\n                    <SelectItem value=\"mx\">Mexico</SelectItem>\n                    <SelectItem value=\"uk\">United Kingdom</SelectItem>\n                  </SelectContent>\n                </Select>\n                {errors.country && (\n                  <p className=\"text-destructive text-sm\">{errors.country.message}</p>\n                )}\n              </div>\n\n              {/* Street Address */}\n              <div className=\"grid gap-2\">\n                <Label htmlFor=\"street\">Street address</Label>\n                <Input id=\"street\" {...register(\"street\")} />\n                {errors.street && (\n                  <p className=\"text-destructive text-sm\">{errors.street.message}</p>\n                )}\n              </div>\n\n              {/* City, State, ZIP */}\n              <div className=\"grid gap-6 sm:grid-cols-3\">\n                <div className=\"grid gap-2\">\n                  <Label htmlFor=\"city\">City</Label>\n                  <Input id=\"city\" {...register(\"city\")} />\n                  {errors.city && <p className=\"text-destructive text-sm\">{errors.city.message}</p>}\n                </div>\n                <div className=\"grid gap-2\">\n                  <Label htmlFor=\"state\">State / Province</Label>\n                  <Input id=\"state\" {...register(\"state\")} />\n                  {errors.state && (\n                    <p className=\"text-destructive text-sm\">{errors.state.message}</p>\n                  )}\n                </div>\n                <div className=\"grid gap-2\">\n                  <Label htmlFor=\"zip\">ZIP / Postal code</Label>\n                  <Input id=\"zip\" {...register(\"zip\")} />\n                  {errors.zip && <p className=\"text-destructive text-sm\">{errors.zip.message}</p>}\n                </div>\n              </div>\n            </div>\n          </div>\n\n          {/* Notifications Section */}\n          <div className=\"border-border space-y-6 border-t pt-12\">\n            <div>\n              <h2 className=\"text-foreground text-xl font-semibold\">Notifications</h2>\n              <p className=\"text-muted-foreground mt-1 text-sm\">\n                We'll always let you know about important changes, but you pick what else you want\n                to hear about.\n              </p>\n            </div>\n\n            <div className=\"space-y-8\">\n              {/* By Email */}\n              <div className=\"space-y-4\">\n                <h3 className=\"text-foreground text-base font-semibold\">By email</h3>\n\n                <div className=\"space-y-4\">\n                  <div className=\"flex items-start gap-3\">\n                    <Checkbox\n                      id=\"comments\"\n                      checked={watchedValues.notifications.comments}\n                      onCheckedChange={(checked) =>\n                        setValue(\"notifications.comments\", checked as boolean)\n                      }\n                      className=\"mt-0.5\"\n                    />\n                    <div className=\"space-y-1\">\n                      <Label\n                        htmlFor=\"comments\"\n                        className=\"text-foreground cursor-pointer font-medium\">\n                        Comments\n                      </Label>\n                      <p className=\"text-muted-foreground text-sm\">\n                        Get notified when someone posts a comment on a posting.\n                      </p>\n                    </div>\n                  </div>\n\n                  <div className=\"flex items-start gap-3\">\n                    <Checkbox\n                      id=\"candidates\"\n                      checked={watchedValues.notifications.candidates}\n                      onCheckedChange={(checked) =>\n                        setValue(\"notifications.candidates\", checked as boolean)\n                      }\n                      className=\"mt-0.5\"\n                    />\n                    <div className=\"space-y-1\">\n                      <Label\n                        htmlFor=\"candidates\"\n                        className=\"text-foreground cursor-pointer font-medium\">\n                        Candidates\n                      </Label>\n                      <p className=\"text-muted-foreground text-sm\">\n                        Get notified when a candidate applies for a job.\n                      </p>\n                    </div>\n                  </div>\n\n                  <div className=\"flex items-start gap-3\">\n                    <Checkbox\n                      id=\"offers\"\n                      checked={watchedValues.notifications.offers}\n                      onCheckedChange={(checked) =>\n                        setValue(\"notifications.offers\", checked as boolean)\n                      }\n                      className=\"mt-0.5\"\n                    />\n                    <div className=\"space-y-1\">\n                      <Label\n                        htmlFor=\"offers\"\n                        className=\"text-foreground cursor-pointer font-medium\">\n                        Offers\n                      </Label>\n                      <p className=\"text-muted-foreground text-sm\">\n                        Get notified when a candidate accepts or rejects an offer.\n                      </p>\n                    </div>\n                  </div>\n                </div>\n              </div>\n\n              {/* Push Notifications */}\n              <div className=\"space-y-4\">\n                <div>\n                  <Label className=\"text-base font-semibold\">Push notifications</Label>\n                  <p className=\"text-muted-foreground mt-1 text-sm\">\n                    These are delivered via SMS to your mobile phone.\n                  </p>\n                </div>\n                <RadioGroup\n                  value={watchedValues.pushNotifications}\n                  onValueChange={(value) => setValue(\"pushNotifications\", value as any)}\n                  className=\"space-y-3\">\n                  <div className=\"flex items-center gap-3\">\n                    <RadioGroupItem value=\"everything\" id=\"everything\" />\n                    <Label\n                      htmlFor=\"everything\"\n                      className=\"text-foreground cursor-pointer font-medium\">\n                      Everything\n                    </Label>\n                  </div>\n                  <div className=\"flex items-center gap-3\">\n                    <RadioGroupItem value=\"same-as-email\" id=\"same-as-email\" />\n                    <Label\n                      htmlFor=\"same-as-email\"\n                      className=\"text-foreground cursor-pointer font-medium\">\n                      Same as email\n                    </Label>\n                  </div>\n                  <div className=\"flex items-center gap-3\">\n                    <RadioGroupItem value=\"no-push\" id=\"no-push\" />\n                    <Label htmlFor=\"no-push\" className=\"text-foreground cursor-pointer font-medium\">\n                      No push notifications\n                    </Label>\n                  </div>\n                </RadioGroup>\n              </div>\n            </div>\n          </div>\n\n          {/* Action Buttons */}\n          <div className=\"border-border flex justify-end gap-3 border-t pt-6\">\n            <Button type=\"button\" variant=\"ghost\">\n              Cancel\n            </Button>\n            <Button\n              type=\"submit\"\n              className=\"bg-primary text-primary-foreground hover:bg-primary/90\">\n              Save\n            </Button>\n          </div>\n        </form>\n      </Form>\n    </div>\n  );\n}\n",
      "type": "registry:component",
      "target": "components/form-layouts-01.tsx"
    }
  ],
  "meta": {
    "isPro": true
  }
}