Setting the file. One moment.
Form Pattern · Shadcn UI · google-labs-code/stitch-skills · Skills Docs
ContentsBack to the top of the page examples/form-pattern.tsx
examples/ form-pattern.tsx
TypeScript · 177 lines · 5 KB
"@/components/ui/form"
16 import { Input } from "@/components/ui/input"
17 import {
18 Select,
19 SelectContent,
20 SelectItem,
21 SelectTrigger,
22 SelectValue,
23 } from "@/components/ui/select"
24 import { Textarea } from "@/components/ui/textarea"
25 import { toast } from "@/components/ui/use-toast"
26 import { zodResolver } from "@hookform/resolvers/zod"
27 import { useForm } from "react-hook-form"
28 import * as z from "zod"
29
30 // Define form schema with zod
31 const formSchema = z. object ({
32 username: z. string (). min ( 2 , {
33 message: "Username must be at least 2 characters." ,
34 }),
35 email: z. string (). email ({
36 message: "Please enter a valid email address." ,
37 }),
38 role: z. enum ([ "admin" , "user" , "viewer" ], {
39 required_error: "Please select a role." ,
40 }),
41 bio: z. string (). max ( 160 , {
42 message: "Bio must not be longer than 160 characters." ,
43 }). optional (),
44 })
45
46 type FormValues = z . infer < typeof formSchema>
47
48 export function UserProfileForm () {
49 // Initialize form with react-hook-form and zod validation
50 const form = useForm < FormValues >({
51 resolver: zodResolver (formSchema),
52 defaultValues: {
53 username: "" ,
54 email: "" ,
55 bio: "" ,
56 },
57 })
58
59 // Handle form submission
60 function onSubmit ( values : FormValues ) {
61 // In a real app, send to API
62 console. log (values)
63
64 toast ({
65 title: "Profile updated" ,
66 description: "Your profile has been successfully updated." ,
67 })
68 }
69
70 return (
71 < Form {... form } >
72 < form onSubmit ={ form. handleSubmit (onSubmit) } className = "space-y-8" >
73 < FormField
74 control ={ form.control }
75 name = "username"
76 render ={ ({ field }) => (
77 < FormItem >
78 < FormLabel >Username</ FormLabel >
79 < FormControl >
80 < Input placeholder = "johndoe" {... field } />
81 </ FormControl >
82 < FormDescription >
83 This is your public display name.
84 </ FormDescription >
85 < FormMessage />
86 </ FormItem >
87 ) }
88 />
89
90 < FormField
91 control ={ form.control }
92 name = "email"
93 render ={ ({ field }) => (
94 < FormItem >
95 < FormLabel >Email</ FormLabel >
96 < FormControl >
97 < Input type = "email" placeholder = "john@example.com" {... field } />
98 </ FormControl >
99 < FormMessage />
100 </ FormItem >
101 ) }
102 />
103
104 < FormField
105 control ={ form.control }
106 name = "role"
107 render ={ ({ field }) => (
108 < FormItem >
109 < FormLabel >Role</ FormLabel >
110 < Select onValueChange ={ field.onChange } defaultValue ={ field.value } >
111 < FormControl >
112 < SelectTrigger >
113 < SelectValue placeholder = "Select a role" />
114 </ SelectTrigger >
115 </ FormControl >
116 < SelectContent >
117 < SelectItem value = "admin" >Admin</ SelectItem >
118 < SelectItem value = "user" >User</ SelectItem >
119 < SelectItem value = "viewer" >Viewer</ SelectItem >
120 </ SelectContent >
121 </ Select >
122 < FormDescription >
123 Your role determines your access level.
124 </ FormDescription >
125 < FormMessage />
126 </ FormItem >
127 ) }
128 />
129
130 < FormField
131 control ={ form.control }
132 name = "bio"
133 render ={ ({ field }) => (
134 < FormItem >
135 < FormLabel >Bio</ FormLabel >
136 < FormControl >
137 < Textarea
138 placeholder = "Tell us about yourself"
139 className = "resize-none"
140 {... field }
141 />
142 </ FormControl >
143 < FormDescription >
144 Optional. Maximum 160 characters.
145 </ FormDescription >
146 < FormMessage />
147 </ FormItem >
148 ) }
149 />
150
151 < Button type = "submit" >Update profile</ Button >
152 </ form >
153 </ Form >
154 )
155 }
156
157 /**
158 * Key Patterns Demonstrated:
159 *
160 * 1. Form Composition: Using shadcn/ui's Form components with react-hook-form
161 * 2. Validation: Zod schema for type-safe validation
162 * 3. Error Handling: Automatic error messages via FormMessage
163 * 4. Accessibility: All fields properly labeled with descriptions
164 * 5. Type Safety: TypeScript types inferred from Zod schema
165 *
166 * Required Dependencies:
167 * - react-hook-form
168 * - @hookform/resolvers
169 * - zod
170 *
171 * Installation:
172 * npx shadcn @latest add form
173 * npx shadcn @latest add input
174 * npx shadcn @latest add select
175 * npx shadcn @latest add textarea
176 * npx shadcn @latest add button
177 */