@adlas/create-app 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (79) hide show
  1. package/README.md +476 -0
  2. package/dist/cli.d.ts +3 -0
  3. package/dist/cli.d.ts.map +1 -0
  4. package/dist/cli.js +39 -0
  5. package/dist/cli.js.map +1 -0
  6. package/dist/commands/figma.d.ts +16 -0
  7. package/dist/commands/figma.d.ts.map +1 -0
  8. package/dist/commands/figma.js +172 -0
  9. package/dist/commands/figma.js.map +1 -0
  10. package/dist/commands/index.d.ts +5 -0
  11. package/dist/commands/index.d.ts.map +1 -0
  12. package/dist/commands/index.js +5 -0
  13. package/dist/commands/index.js.map +1 -0
  14. package/dist/commands/init.d.ts +8 -0
  15. package/dist/commands/init.d.ts.map +1 -0
  16. package/dist/commands/init.js +1471 -0
  17. package/dist/commands/init.js.map +1 -0
  18. package/dist/commands/swagger.d.ts +16 -0
  19. package/dist/commands/swagger.d.ts.map +1 -0
  20. package/dist/commands/swagger.js +404 -0
  21. package/dist/commands/swagger.js.map +1 -0
  22. package/dist/commands/update.d.ts +15 -0
  23. package/dist/commands/update.d.ts.map +1 -0
  24. package/dist/commands/update.js +93 -0
  25. package/dist/commands/update.js.map +1 -0
  26. package/package.json +63 -0
  27. package/templates/.vscode/extensions.json +9 -0
  28. package/templates/.vscode/launch.json +26 -0
  29. package/templates/.vscode/settings.json +67 -0
  30. package/templates/.vscode/tasks.json +21 -0
  31. package/templates/boilerplate/config/fonts.ts +10 -0
  32. package/templates/boilerplate/config/navigationUrls.ts +47 -0
  33. package/templates/boilerplate/config/site.ts +96 -0
  34. package/templates/boilerplate/libs/I18n.ts +15 -0
  35. package/templates/boilerplate/libs/I18nNavigation.ts +5 -0
  36. package/templates/boilerplate/libs/I18nRouting.ts +9 -0
  37. package/templates/boilerplate/libs/env.ts +21 -0
  38. package/templates/boilerplate/libs/react-query/ReactQueryProvider.tsx +21 -0
  39. package/templates/boilerplate/libs/react-query/index.ts +2 -0
  40. package/templates/boilerplate/libs/react-query/queryClient.ts +62 -0
  41. package/templates/boilerplate/libs/react-query/queryKeys.ts +5 -0
  42. package/templates/boilerplate/public/images/index.ts +1 -0
  43. package/templates/boilerplate/reset.d.ts +2 -0
  44. package/templates/boilerplate/styles/globals.css +308 -0
  45. package/templates/boilerplate/types/i18n.ts +10 -0
  46. package/templates/boilerplate/types/locale.ts +8 -0
  47. package/templates/boilerplate/utils/file/fileConfig.ts +123 -0
  48. package/templates/boilerplate/utils/file/fileValidation.ts +78 -0
  49. package/templates/boilerplate/utils/file/imageCompression.ts +182 -0
  50. package/templates/boilerplate/utils/file/index.ts +3 -0
  51. package/templates/boilerplate/utils/helpers.ts +55 -0
  52. package/templates/boilerplate/validations/auth.validation.ts +92 -0
  53. package/templates/boilerplate/validations/commonValidations.ts +258 -0
  54. package/templates/boilerplate/validations/zodErrorMap.ts +101 -0
  55. package/templates/configs/.env.example +8 -0
  56. package/templates/configs/.prettierignore +23 -0
  57. package/templates/configs/.prettierrc.cjs +26 -0
  58. package/templates/configs/.prettierrc.icons.cjs +11 -0
  59. package/templates/configs/Dockerfile +6 -0
  60. package/templates/configs/commitlint.config.ts +8 -0
  61. package/templates/configs/eslint.config.mjs +119 -0
  62. package/templates/configs/knip.config.ts +32 -0
  63. package/templates/configs/lefthook.yml +42 -0
  64. package/templates/configs/lint-staged.config.js +8 -0
  65. package/templates/configs/next.config.template.ts +77 -0
  66. package/templates/configs/next.config.ts +43 -0
  67. package/templates/configs/package.json +75 -0
  68. package/templates/configs/postcss.config.mjs +15 -0
  69. package/templates/configs/svgr.config.mjs +129 -0
  70. package/templates/configs/tsconfig.json +75 -0
  71. package/templates/docs/AI_QUICK_REFERENCE.md +379 -0
  72. package/templates/docs/ARCHITECTURE_PATTERNS.md +927 -0
  73. package/templates/docs/DOCUMENTATION_INDEX.md +411 -0
  74. package/templates/docs/FIGMA_TO_CODE_GUIDE.md +768 -0
  75. package/templates/docs/IMPLEMENTATION_GUIDE.md +892 -0
  76. package/templates/docs/PROJECT_OVERVIEW.md +302 -0
  77. package/templates/docs/REFACTOR_PROGRESS.md +1113 -0
  78. package/templates/docs/SHADCN_TO_HEROUI_MIGRATION.md +1375 -0
  79. package/templates/docs/UI_COMPONENTS_GUIDE.md +893 -0
@@ -0,0 +1,1375 @@
1
+ # shadcn/ui to HeroUI Migration Guide
2
+
3
+ This guide provides complete component mappings and conversion patterns for migrating from shadcn/ui to HeroUI.
4
+
5
+ ---
6
+
7
+ ## 📚 Table of Contents
8
+
9
+ 1. [Key Differences](#key-differences)
10
+ 2. [Installation & Setup](#installation--setup)
11
+ 3. [Component Mappings](#component-mappings)
12
+ 4. [Common Patterns](#common-patterns)
13
+ 5. [Migration Checklist](#migration-checklist)
14
+
15
+ ---
16
+
17
+ ## Key Differences
18
+
19
+ ### Philosophy
20
+ - **shadcn/ui:** Copy-paste components into your project, full customization
21
+ - **HeroUI:** NPM package components, theme-based customization
22
+
23
+ ### Styling Approach
24
+ - **shadcn/ui:** Uses `cn()` utility for merging Tailwind classes
25
+ - **HeroUI:** Uses `classNames` prop for different component parts
26
+
27
+ ### Import Pattern
28
+ ```tsx
29
+ // shadcn/ui
30
+ import { Button } from "@/components/ui/button"
31
+
32
+ // HeroUI
33
+ import { Button } from "@heroui/button"
34
+ // or
35
+ import { Button } from "@heroui/react"
36
+ ```
37
+
38
+ ### Props Philosophy
39
+ - **shadcn/ui:** Single `className` prop
40
+ - **HeroUI:** Structured `classNames` object for different parts
41
+
42
+ ---
43
+
44
+ ## Installation & Setup
45
+
46
+ ### Remove shadcn/ui
47
+ ```bash
48
+ # Remove shadcn components folder
49
+ rm -rf src/components/ui/
50
+
51
+ # Remove from package.json (if added as dependency)
52
+ pnpm remove @radix-ui/react-* class-variance-authority clsx tailwind-merge
53
+ ```
54
+
55
+ ### Install HeroUI
56
+ ```bash
57
+ # Already installed in this project
58
+ # If needed in target project:
59
+ pnpm add @heroui/react framer-motion
60
+ ```
61
+
62
+ ### Update tailwind.config.js
63
+ ```js
64
+ // Remove shadcn config
65
+ // Add HeroUI config (already in this project)
66
+ import { heroui } from "@heroui/react"
67
+
68
+ module.exports = {
69
+ plugins: [heroui()],
70
+ }
71
+ ```
72
+
73
+ ---
74
+
75
+ ## Component Mappings
76
+
77
+ ### Form Components
78
+
79
+ #### Input
80
+
81
+ **shadcn/ui:**
82
+ ```tsx
83
+ import { Input } from "@/components/ui/input"
84
+ import { Label } from "@/components/ui/label"
85
+
86
+ <div className="space-y-2">
87
+ <Label htmlFor="email">Email</Label>
88
+ <Input
89
+ id="email"
90
+ type="email"
91
+ placeholder="Enter your email"
92
+ className="w-full"
93
+ />
94
+ </div>
95
+ ```
96
+
97
+ **HeroUI:**
98
+ ```tsx
99
+ import { Input } from "@heroui/input"
100
+
101
+ <Input
102
+ type="email"
103
+ label="Email"
104
+ placeholder="Enter your email"
105
+ variant="bordered"
106
+ classNames={{
107
+ input: "w-full",
108
+ }}
109
+ />
110
+ ```
111
+
112
+ **Key Changes:**
113
+ - ✅ Label is built-in via `label` prop
114
+ - ✅ Use `variant="bordered"` for outlined style
115
+ - ✅ Use `classNames` object instead of `className`
116
+
117
+ **Common Variants:**
118
+ ```tsx
119
+ // Bordered (most common - similar to shadcn default)
120
+ <Input variant="bordered" />
121
+
122
+ // Flat
123
+ <Input variant="flat" />
124
+
125
+ // Faded
126
+ <Input variant="faded" />
127
+
128
+ // Underlined
129
+ <Input variant="underlined" />
130
+ ```
131
+
132
+ **With Error State:**
133
+ ```tsx
134
+ <Input
135
+ type="email"
136
+ label="Email"
137
+ variant="bordered"
138
+ isInvalid={!!errors.email}
139
+ errorMessage={errors.email?.message}
140
+ />
141
+ ```
142
+
143
+ ---
144
+
145
+ #### Textarea
146
+
147
+ **shadcn/ui:**
148
+ ```tsx
149
+ import { Textarea } from "@/components/ui/textarea"
150
+
151
+ <Textarea
152
+ placeholder="Enter description"
153
+ className="min-h-[100px]"
154
+ />
155
+ ```
156
+
157
+ **HeroUI:**
158
+ ```tsx
159
+ import { Textarea } from "@heroui/input"
160
+
161
+ <Textarea
162
+ label="Description"
163
+ placeholder="Enter description"
164
+ variant="bordered"
165
+ minRows={3}
166
+ />
167
+ ```
168
+
169
+ ---
170
+
171
+ #### Button
172
+
173
+ **shadcn/ui:**
174
+ ```tsx
175
+ import { Button } from "@/components/ui/button"
176
+
177
+ <Button variant="default" size="lg">
178
+ Click me
179
+ </Button>
180
+
181
+ <Button variant="destructive">
182
+ Delete
183
+ </Button>
184
+
185
+ <Button variant="outline">
186
+ Cancel
187
+ </Button>
188
+
189
+ <Button variant="ghost">
190
+ Ghost
191
+ </Button>
192
+ ```
193
+
194
+ **HeroUI:**
195
+ ```tsx
196
+ import { Button } from "@heroui/button"
197
+
198
+ <Button color="primary" size="lg">
199
+ Click me
200
+ </Button>
201
+
202
+ <Button color="danger">
203
+ Delete
204
+ </Button>
205
+
206
+ <Button variant="bordered">
207
+ Cancel
208
+ </Button>
209
+
210
+ <Button variant="light">
211
+ Ghost
212
+ </Button>
213
+ ```
214
+
215
+ **Variant Mapping:**
216
+ | shadcn/ui | HeroUI |
217
+ |-----------|---------|
218
+ | `variant="default"` | `color="primary"` |
219
+ | `variant="destructive"` | `color="danger"` |
220
+ | `variant="outline"` | `variant="bordered"` |
221
+ | `variant="ghost"` | `variant="light"` |
222
+ | `variant="secondary"` | `color="secondary"` |
223
+
224
+ **With Loading State:**
225
+ ```tsx
226
+ // shadcn/ui
227
+ <Button disabled={isLoading}>
228
+ {isLoading && <Loader2 className="mr-2 h-4 w-4 animate-spin" />}
229
+ Submit
230
+ </Button>
231
+
232
+ // HeroUI
233
+ <Button isLoading={isLoading}>
234
+ Submit
235
+ </Button>
236
+ ```
237
+
238
+ ---
239
+
240
+ #### Select
241
+
242
+ **shadcn/ui:**
243
+ ```tsx
244
+ import {
245
+ Select,
246
+ SelectContent,
247
+ SelectItem,
248
+ SelectTrigger,
249
+ SelectValue,
250
+ } from "@/components/ui/select"
251
+
252
+ <Select onValueChange={setValue}>
253
+ <SelectTrigger>
254
+ <SelectValue placeholder="Select option" />
255
+ </SelectTrigger>
256
+ <SelectContent>
257
+ <SelectItem value="option1">Option 1</SelectItem>
258
+ <SelectItem value="option2">Option 2</SelectItem>
259
+ </SelectContent>
260
+ </Select>
261
+ ```
262
+
263
+ **HeroUI:**
264
+ ```tsx
265
+ import { Select, SelectItem } from "@heroui/select"
266
+
267
+ <Select
268
+ label="Select option"
269
+ placeholder="Choose an option"
270
+ variant="bordered"
271
+ onChange={(e) => setValue(e.target.value)}
272
+ >
273
+ <SelectItem key="option1" value="option1">
274
+ Option 1
275
+ </SelectItem>
276
+ <SelectItem key="option2" value="option2">
277
+ Option 2
278
+ </SelectItem>
279
+ </Select>
280
+ ```
281
+
282
+ **Dynamic Options:**
283
+ ```tsx
284
+ const options = [
285
+ { value: "1", label: "Option 1" },
286
+ { value: "2", label: "Option 2" },
287
+ ]
288
+
289
+ <Select
290
+ label="Select option"
291
+ variant="bordered"
292
+ items={options}
293
+ >
294
+ {(item) => (
295
+ <SelectItem key={item.value}>
296
+ {item.label}
297
+ </SelectItem>
298
+ )}
299
+ </Select>
300
+ ```
301
+
302
+ ---
303
+
304
+ #### Checkbox
305
+
306
+ **shadcn/ui:**
307
+ ```tsx
308
+ import { Checkbox } from "@/components/ui/checkbox"
309
+
310
+ <div className="flex items-center space-x-2">
311
+ <Checkbox id="terms" />
312
+ <label htmlFor="terms">Accept terms</label>
313
+ </div>
314
+ ```
315
+
316
+ **HeroUI:**
317
+ ```tsx
318
+ import { Checkbox } from "@heroui/checkbox"
319
+
320
+ <Checkbox>Accept terms</Checkbox>
321
+ ```
322
+
323
+ **Controlled:**
324
+ ```tsx
325
+ <Checkbox
326
+ isSelected={isAccepted}
327
+ onValueChange={setIsAccepted}
328
+ >
329
+ Accept terms
330
+ </Checkbox>
331
+ ```
332
+
333
+ ---
334
+
335
+ #### Radio Group
336
+
337
+ **shadcn/ui:**
338
+ ```tsx
339
+ import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group"
340
+
341
+ <RadioGroup value={value} onValueChange={setValue}>
342
+ <div className="flex items-center space-x-2">
343
+ <RadioGroupItem value="option1" id="option1" />
344
+ <Label htmlFor="option1">Option 1</Label>
345
+ </div>
346
+ <div className="flex items-center space-x-2">
347
+ <RadioGroupItem value="option2" id="option2" />
348
+ <Label htmlFor="option2">Option 2</Label>
349
+ </div>
350
+ </RadioGroup>
351
+ ```
352
+
353
+ **HeroUI:**
354
+ ```tsx
355
+ import { RadioGroup, Radio } from "@heroui/radio"
356
+
357
+ <RadioGroup
358
+ label="Select option"
359
+ value={value}
360
+ onValueChange={setValue}
361
+ >
362
+ <Radio value="option1">Option 1</Radio>
363
+ <Radio value="option2">Option 2</Radio>
364
+ </RadioGroup>
365
+ ```
366
+
367
+ ---
368
+
369
+ #### Switch
370
+
371
+ **shadcn/ui:**
372
+ ```tsx
373
+ import { Switch } from "@/components/ui/switch"
374
+
375
+ <Switch checked={isEnabled} onCheckedChange={setIsEnabled} />
376
+ ```
377
+
378
+ **HeroUI:**
379
+ ```tsx
380
+ import { Switch } from "@heroui/switch"
381
+
382
+ <Switch isSelected={isEnabled} onValueChange={setIsEnabled}>
383
+ Enable feature
384
+ </Switch>
385
+ ```
386
+
387
+ ---
388
+
389
+ #### Slider
390
+
391
+ **shadcn/ui:**
392
+ ```tsx
393
+ import { Slider } from "@/components/ui/slider"
394
+
395
+ <Slider
396
+ value={[value]}
397
+ onValueChange={([val]) => setValue(val)}
398
+ max={100}
399
+ step={1}
400
+ />
401
+ ```
402
+
403
+ **HeroUI:**
404
+ ```tsx
405
+ import { Slider } from "@heroui/slider"
406
+
407
+ <Slider
408
+ label="Price Range"
409
+ value={value}
410
+ onChange={setValue}
411
+ maxValue={100}
412
+ step={1}
413
+ formatOptions={{ style: "currency", currency: "EUR" }}
414
+ />
415
+ ```
416
+
417
+ **Range Slider:**
418
+ ```tsx
419
+ <Slider
420
+ label="Price Range"
421
+ value={[minPrice, maxPrice]}
422
+ onChange={([min, max]) => {
423
+ setMinPrice(min)
424
+ setMaxPrice(max)
425
+ }}
426
+ minValue={0}
427
+ maxValue={1000}
428
+ step={10}
429
+ />
430
+ ```
431
+
432
+ ---
433
+
434
+ ### Display Components
435
+
436
+ #### Card
437
+
438
+ **shadcn/ui:**
439
+ ```tsx
440
+ import {
441
+ Card,
442
+ CardContent,
443
+ CardDescription,
444
+ CardFooter,
445
+ CardHeader,
446
+ CardTitle,
447
+ } from "@/components/ui/card"
448
+
449
+ <Card>
450
+ <CardHeader>
451
+ <CardTitle>Title</CardTitle>
452
+ <CardDescription>Description</CardDescription>
453
+ </CardHeader>
454
+ <CardContent>
455
+ Content goes here
456
+ </CardContent>
457
+ <CardFooter>
458
+ Footer content
459
+ </CardFooter>
460
+ </Card>
461
+ ```
462
+
463
+ **HeroUI:**
464
+ ```tsx
465
+ import { Card, CardHeader, CardBody, CardFooter } from "@heroui/card"
466
+
467
+ <Card>
468
+ <CardHeader className="flex flex-col items-start">
469
+ <h4 className="text-lg font-bold">Title</h4>
470
+ <p className="text-sm text-default-500">Description</p>
471
+ </CardHeader>
472
+ <CardBody>
473
+ Content goes here
474
+ </CardBody>
475
+ <CardFooter>
476
+ Footer content
477
+ </CardFooter>
478
+ </Card>
479
+ ```
480
+
481
+ **Variants:**
482
+ ```tsx
483
+ // Bordered
484
+ <Card variant="bordered">...</Card>
485
+
486
+ // Elevated (shadow)
487
+ <Card shadow="md">...</Card>
488
+
489
+ // Hoverable
490
+ <Card isHoverable isPressable>...</Card>
491
+ ```
492
+
493
+ ---
494
+
495
+ #### Badge / Chip
496
+
497
+ **shadcn/ui:**
498
+ ```tsx
499
+ import { Badge } from "@/components/ui/badge"
500
+
501
+ <Badge variant="default">Badge</Badge>
502
+ <Badge variant="destructive">Error</Badge>
503
+ <Badge variant="outline">Outline</Badge>
504
+ ```
505
+
506
+ **HeroUI:**
507
+ ```tsx
508
+ import { Chip } from "@heroui/chip"
509
+
510
+ <Chip color="primary">Chip</Chip>
511
+ <Chip color="danger">Error</Chip>
512
+ <Chip variant="bordered">Outline</Chip>
513
+ ```
514
+
515
+ **With Close Button:**
516
+ ```tsx
517
+ <Chip
518
+ onClose={() => console.log("closed")}
519
+ variant="flat"
520
+ >
521
+ Closeable
522
+ </Chip>
523
+ ```
524
+
525
+ **As Status:**
526
+ ```tsx
527
+ <Chip color="success" variant="dot">Active</Chip>
528
+ <Chip color="warning" variant="dot">Pending</Chip>
529
+ <Chip color="danger" variant="dot">Inactive</Chip>
530
+ ```
531
+
532
+ ---
533
+
534
+ #### Avatar
535
+
536
+ **shadcn/ui:**
537
+ ```tsx
538
+ import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar"
539
+
540
+ <Avatar>
541
+ <AvatarImage src="/avatar.jpg" alt="User" />
542
+ <AvatarFallback>JD</AvatarFallback>
543
+ </Avatar>
544
+ ```
545
+
546
+ **HeroUI:**
547
+ ```tsx
548
+ import { Avatar } from "@heroui/avatar"
549
+
550
+ <Avatar
551
+ src="/avatar.jpg"
552
+ alt="User"
553
+ showFallback
554
+ name="John Doe"
555
+ />
556
+ ```
557
+
558
+ **Sizes:**
559
+ ```tsx
560
+ <Avatar size="sm" />
561
+ <Avatar size="md" />
562
+ <Avatar size="lg" />
563
+ ```
564
+
565
+ **With Badge:**
566
+ ```tsx
567
+ <Avatar
568
+ isBordered
569
+ color="success"
570
+ src="/avatar.jpg"
571
+ />
572
+ ```
573
+
574
+ ---
575
+
576
+ #### Alert / Callout
577
+
578
+ **shadcn/ui:**
579
+ ```tsx
580
+ import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert"
581
+
582
+ <Alert variant="destructive">
583
+ <AlertCircle className="h-4 w-4" />
584
+ <AlertTitle>Error</AlertTitle>
585
+ <AlertDescription>
586
+ Something went wrong
587
+ </AlertDescription>
588
+ </Alert>
589
+ ```
590
+
591
+ **HeroUI:**
592
+ ```tsx
593
+ // HeroUI doesn't have Alert component
594
+ // Use custom component or Card with colors
595
+
596
+ import { Card, CardBody } from "@heroui/card"
597
+
598
+ <Card className="bg-danger-50 border-l-4 border-danger">
599
+ <CardBody>
600
+ <div className="flex gap-2">
601
+ <AlertCircle className="text-danger" />
602
+ <div>
603
+ <h4 className="text-danger font-semibold">Error</h4>
604
+ <p className="text-danger-600">Something went wrong</p>
605
+ </div>
606
+ </div>
607
+ </CardBody>
608
+ </Card>
609
+ ```
610
+
611
+ **Or create reusable Alert component:**
612
+ ```tsx
613
+ // components/ui/Alert.tsx
614
+ import { Card, CardBody } from "@heroui/card"
615
+ import { cn } from "@/lib/utils"
616
+
617
+ type AlertProps = {
618
+ variant?: "info" | "success" | "warning" | "danger"
619
+ title?: string
620
+ children: React.ReactNode
621
+ }
622
+
623
+ export function Alert({ variant = "info", title, children }: AlertProps) {
624
+ const colors = {
625
+ info: "bg-primary-50 border-primary text-primary-900",
626
+ success: "bg-success-50 border-success text-success-900",
627
+ warning: "bg-warning-50 border-warning text-warning-900",
628
+ danger: "bg-danger-50 border-danger text-danger-900",
629
+ }
630
+
631
+ return (
632
+ <Card className={cn("border-l-4", colors[variant])}>
633
+ <CardBody>
634
+ {title && <h4 className="font-semibold mb-1">{title}</h4>}
635
+ <div>{children}</div>
636
+ </CardBody>
637
+ </Card>
638
+ )
639
+ }
640
+ ```
641
+
642
+ ---
643
+
644
+ ### Navigation Components
645
+
646
+ #### Tabs
647
+
648
+ **shadcn/ui:**
649
+ ```tsx
650
+ import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"
651
+
652
+ <Tabs defaultValue="tab1">
653
+ <TabsList>
654
+ <TabsTrigger value="tab1">Tab 1</TabsTrigger>
655
+ <TabsTrigger value="tab2">Tab 2</TabsTrigger>
656
+ </TabsList>
657
+ <TabsContent value="tab1">
658
+ Content 1
659
+ </TabsContent>
660
+ <TabsContent value="tab2">
661
+ Content 2
662
+ </TabsContent>
663
+ </Tabs>
664
+ ```
665
+
666
+ **HeroUI:**
667
+ ```tsx
668
+ import { Tabs, Tab } from "@heroui/tabs"
669
+
670
+ <Tabs>
671
+ <Tab key="tab1" title="Tab 1">
672
+ Content 1
673
+ </Tab>
674
+ <Tab key="tab2" title="Tab 2">
675
+ Content 2
676
+ </Tab>
677
+ </Tabs>
678
+ ```
679
+
680
+ **Controlled:**
681
+ ```tsx
682
+ <Tabs
683
+ selectedKey={selected}
684
+ onSelectionChange={setSelected}
685
+ >
686
+ <Tab key="tab1" title="Tab 1">Content 1</Tab>
687
+ <Tab key="tab2" title="Tab 2">Content 2</Tab>
688
+ </Tabs>
689
+ ```
690
+
691
+ **With Icons:**
692
+ ```tsx
693
+ import { User, Settings } from "lucide-react"
694
+
695
+ <Tabs>
696
+ <Tab
697
+ key="profile"
698
+ title={
699
+ <div className="flex items-center gap-2">
700
+ <User size={16} />
701
+ Profile
702
+ </div>
703
+ }
704
+ >
705
+ Profile content
706
+ </Tab>
707
+ <Tab
708
+ key="settings"
709
+ title={
710
+ <div className="flex items-center gap-2">
711
+ <Settings size={16} />
712
+ Settings
713
+ </div>
714
+ }
715
+ >
716
+ Settings content
717
+ </Tab>
718
+ </Tabs>
719
+ ```
720
+
721
+ ---
722
+
723
+ #### Dropdown Menu
724
+
725
+ **shadcn/ui:**
726
+ ```tsx
727
+ import {
728
+ DropdownMenu,
729
+ DropdownMenuContent,
730
+ DropdownMenuItem,
731
+ DropdownMenuTrigger,
732
+ } from "@/components/ui/dropdown-menu"
733
+
734
+ <DropdownMenu>
735
+ <DropdownMenuTrigger asChild>
736
+ <Button variant="outline">Open</Button>
737
+ </DropdownMenuTrigger>
738
+ <DropdownMenuContent>
739
+ <DropdownMenuItem>Item 1</DropdownMenuItem>
740
+ <DropdownMenuItem>Item 2</DropdownMenuItem>
741
+ </DropdownMenuContent>
742
+ </DropdownMenu>
743
+ ```
744
+
745
+ **HeroUI:**
746
+ ```tsx
747
+ import {
748
+ Dropdown,
749
+ DropdownTrigger,
750
+ DropdownMenu,
751
+ DropdownItem,
752
+ } from "@heroui/dropdown"
753
+ import { Button } from "@heroui/button"
754
+
755
+ <Dropdown>
756
+ <DropdownTrigger>
757
+ <Button variant="bordered">Open</Button>
758
+ </DropdownTrigger>
759
+ <DropdownMenu>
760
+ <DropdownItem key="item1">Item 1</DropdownItem>
761
+ <DropdownItem key="item2">Item 2</DropdownItem>
762
+ </DropdownMenu>
763
+ </Dropdown>
764
+ ```
765
+
766
+ **With Actions:**
767
+ ```tsx
768
+ <DropdownMenu
769
+ onAction={(key) => {
770
+ if (key === "edit") handleEdit()
771
+ if (key === "delete") handleDelete()
772
+ }}
773
+ >
774
+ <DropdownItem key="edit">Edit</DropdownItem>
775
+ <DropdownItem key="delete" color="danger">
776
+ Delete
777
+ </DropdownItem>
778
+ </DropdownMenu>
779
+ ```
780
+
781
+ ---
782
+
783
+ #### Breadcrumbs
784
+
785
+ **shadcn/ui:**
786
+ ```tsx
787
+ // Usually custom implementation
788
+ <nav className="flex">
789
+ <Link href="/">Home</Link>
790
+ <span>/</span>
791
+ <Link href="/products">Products</Link>
792
+ <span>/</span>
793
+ <span>Current</span>
794
+ </nav>
795
+ ```
796
+
797
+ **HeroUI:**
798
+ ```tsx
799
+ import { Breadcrumbs, BreadcrumbItem } from "@heroui/breadcrumbs"
800
+
801
+ <Breadcrumbs>
802
+ <BreadcrumbItem href="/">Home</BreadcrumbItem>
803
+ <BreadcrumbItem href="/products">Products</BreadcrumbItem>
804
+ <BreadcrumbItem>Current</BreadcrumbItem>
805
+ </Breadcrumbs>
806
+ ```
807
+
808
+ ---
809
+
810
+ ### Overlay Components
811
+
812
+ #### Dialog / Modal
813
+
814
+ **shadcn/ui:**
815
+ ```tsx
816
+ import {
817
+ Dialog,
818
+ DialogContent,
819
+ DialogDescription,
820
+ DialogHeader,
821
+ DialogTitle,
822
+ DialogTrigger,
823
+ } from "@/components/ui/dialog"
824
+
825
+ <Dialog open={isOpen} onOpenChange={setIsOpen}>
826
+ <DialogTrigger asChild>
827
+ <Button>Open Dialog</Button>
828
+ </DialogTrigger>
829
+ <DialogContent>
830
+ <DialogHeader>
831
+ <DialogTitle>Title</DialogTitle>
832
+ <DialogDescription>Description</DialogDescription>
833
+ </DialogHeader>
834
+ <div>Content</div>
835
+ </DialogContent>
836
+ </Dialog>
837
+ ```
838
+
839
+ **HeroUI:**
840
+ ```tsx
841
+ import {
842
+ Modal,
843
+ ModalContent,
844
+ ModalHeader,
845
+ ModalBody,
846
+ ModalFooter,
847
+ useDisclosure,
848
+ } from "@heroui/modal"
849
+ import { Button } from "@heroui/button"
850
+
851
+ const { isOpen, onOpen, onClose } = useDisclosure()
852
+
853
+ <>
854
+ <Button onPress={onOpen}>Open Modal</Button>
855
+ <Modal isOpen={isOpen} onClose={onClose}>
856
+ <ModalContent>
857
+ <ModalHeader>Title</ModalHeader>
858
+ <ModalBody>
859
+ <p>Content goes here</p>
860
+ </ModalBody>
861
+ <ModalFooter>
862
+ <Button variant="light" onPress={onClose}>
863
+ Cancel
864
+ </Button>
865
+ <Button color="primary" onPress={onClose}>
866
+ Confirm
867
+ </Button>
868
+ </ModalFooter>
869
+ </ModalContent>
870
+ </Modal>
871
+ </>
872
+ ```
873
+
874
+ **Key Differences:**
875
+ - Use `useDisclosure()` hook for state management
876
+ - ModalContent wraps all content
877
+ - More structured layout with Header/Body/Footer
878
+
879
+ ---
880
+
881
+ #### Popover
882
+
883
+ **shadcn/ui:**
884
+ ```tsx
885
+ import {
886
+ Popover,
887
+ PopoverContent,
888
+ PopoverTrigger,
889
+ } from "@/components/ui/popover"
890
+
891
+ <Popover>
892
+ <PopoverTrigger asChild>
893
+ <Button>Open</Button>
894
+ </PopoverTrigger>
895
+ <PopoverContent>
896
+ Content here
897
+ </PopoverContent>
898
+ </Popover>
899
+ ```
900
+
901
+ **HeroUI:**
902
+ ```tsx
903
+ import { Popover, PopoverTrigger, PopoverContent } from "@heroui/popover"
904
+ import { Button } from "@heroui/button"
905
+
906
+ <Popover>
907
+ <PopoverTrigger>
908
+ <Button>Open</Button>
909
+ </PopoverTrigger>
910
+ <PopoverContent>
911
+ <div className="p-2">
912
+ Content here
913
+ </div>
914
+ </PopoverContent>
915
+ </Popover>
916
+ ```
917
+
918
+ ---
919
+
920
+ #### Tooltip
921
+
922
+ **shadcn/ui:**
923
+ ```tsx
924
+ import {
925
+ Tooltip,
926
+ TooltipContent,
927
+ TooltipProvider,
928
+ TooltipTrigger,
929
+ } from "@/components/ui/tooltip"
930
+
931
+ <TooltipProvider>
932
+ <Tooltip>
933
+ <TooltipTrigger asChild>
934
+ <Button>Hover me</Button>
935
+ </TooltipTrigger>
936
+ <TooltipContent>
937
+ Tooltip text
938
+ </TooltipContent>
939
+ </Tooltip>
940
+ </TooltipProvider>
941
+ ```
942
+
943
+ **HeroUI:**
944
+ ```tsx
945
+ import { Tooltip } from "@heroui/tooltip"
946
+ import { Button } from "@heroui/button"
947
+
948
+ <Tooltip content="Tooltip text">
949
+ <Button>Hover me</Button>
950
+ </Tooltip>
951
+ ```
952
+
953
+ **Much simpler!** No provider needed.
954
+
955
+ ---
956
+
957
+ ### Feedback Components
958
+
959
+ #### Progress
960
+
961
+ **shadcn/ui:**
962
+ ```tsx
963
+ import { Progress } from "@/components/ui/progress"
964
+
965
+ <Progress value={progress} max={100} />
966
+ ```
967
+
968
+ **HeroUI:**
969
+ ```tsx
970
+ import { Progress } from "@heroui/progress"
971
+
972
+ <Progress
973
+ value={progress}
974
+ maxValue={100}
975
+ color="primary"
976
+ showValueLabel
977
+ />
978
+ ```
979
+
980
+ **Circular Progress:**
981
+ ```tsx
982
+ import { CircularProgress } from "@heroui/progress"
983
+
984
+ <CircularProgress
985
+ value={progress}
986
+ color="primary"
987
+ showValueLabel
988
+ />
989
+ ```
990
+
991
+ ---
992
+
993
+ #### Skeleton
994
+
995
+ **shadcn/ui:**
996
+ ```tsx
997
+ import { Skeleton } from "@/components/ui/skeleton"
998
+
999
+ <Skeleton className="h-12 w-12 rounded-full" />
1000
+ <Skeleton className="h-4 w-[250px]" />
1001
+ ```
1002
+
1003
+ **HeroUI:**
1004
+ ```tsx
1005
+ import { Skeleton } from "@heroui/skeleton"
1006
+
1007
+ <Skeleton className="rounded-full w-12 h-12" />
1008
+ <Skeleton className="h-4 w-[250px] rounded-lg" />
1009
+ ```
1010
+
1011
+ **With isLoaded:**
1012
+ ```tsx
1013
+ <Skeleton isLoaded={!isLoading} className="rounded-lg">
1014
+ <div className="h-24 bg-default-200"></div>
1015
+ </Skeleton>
1016
+ ```
1017
+
1018
+ ---
1019
+
1020
+ #### Spinner
1021
+
1022
+ **shadcn/ui:**
1023
+ ```tsx
1024
+ // Usually custom or lucide-react Loader2
1025
+ import { Loader2 } from "lucide-react"
1026
+
1027
+ <Loader2 className="animate-spin" />
1028
+ ```
1029
+
1030
+ **HeroUI:**
1031
+ ```tsx
1032
+ import { Spinner } from "@heroui/spinner"
1033
+
1034
+ <Spinner />
1035
+ <Spinner size="sm" />
1036
+ <Spinner size="lg" />
1037
+ <Spinner color="primary" />
1038
+ ```
1039
+
1040
+ ---
1041
+
1042
+ ### Data Display Components
1043
+
1044
+ #### Table
1045
+
1046
+ **shadcn/ui:**
1047
+ ```tsx
1048
+ import {
1049
+ Table,
1050
+ TableBody,
1051
+ TableCell,
1052
+ TableHead,
1053
+ TableHeader,
1054
+ TableRow,
1055
+ } from "@/components/ui/table"
1056
+
1057
+ <Table>
1058
+ <TableHeader>
1059
+ <TableRow>
1060
+ <TableHead>Name</TableHead>
1061
+ <TableHead>Email</TableHead>
1062
+ </TableRow>
1063
+ </TableHeader>
1064
+ <TableBody>
1065
+ <TableRow>
1066
+ <TableCell>John</TableCell>
1067
+ <TableCell>john@example.com</TableCell>
1068
+ </TableRow>
1069
+ </TableBody>
1070
+ </Table>
1071
+ ```
1072
+
1073
+ **HeroUI:**
1074
+ ```tsx
1075
+ import {
1076
+ Table,
1077
+ TableHeader,
1078
+ TableColumn,
1079
+ TableBody,
1080
+ TableRow,
1081
+ TableCell,
1082
+ } from "@heroui/table"
1083
+
1084
+ <Table>
1085
+ <TableHeader>
1086
+ <TableColumn>NAME</TableColumn>
1087
+ <TableColumn>EMAIL</TableColumn>
1088
+ </TableHeader>
1089
+ <TableBody>
1090
+ <TableRow key="1">
1091
+ <TableCell>John</TableCell>
1092
+ <TableCell>john@example.com</TableCell>
1093
+ </TableRow>
1094
+ </TableBody>
1095
+ </Table>
1096
+ ```
1097
+
1098
+ **Dynamic Rendering:**
1099
+ ```tsx
1100
+ const columns = [
1101
+ { key: "name", label: "NAME" },
1102
+ { key: "email", label: "EMAIL" },
1103
+ ]
1104
+
1105
+ const rows = [
1106
+ { id: 1, name: "John", email: "john@example.com" },
1107
+ { id: 2, name: "Jane", email: "jane@example.com" },
1108
+ ]
1109
+
1110
+ <Table>
1111
+ <TableHeader columns={columns}>
1112
+ {(column) => <TableColumn key={column.key}>{column.label}</TableColumn>}
1113
+ </TableHeader>
1114
+ <TableBody items={rows}>
1115
+ {(item) => (
1116
+ <TableRow key={item.id}>
1117
+ {(columnKey) => <TableCell>{item[columnKey]}</TableCell>}
1118
+ </TableRow>
1119
+ )}
1120
+ </TableBody>
1121
+ </Table>
1122
+ ```
1123
+
1124
+ ---
1125
+
1126
+ #### Pagination
1127
+
1128
+ **shadcn/ui:**
1129
+ ```tsx
1130
+ // Usually custom implementation
1131
+ ```
1132
+
1133
+ **HeroUI:**
1134
+ ```tsx
1135
+ import { Pagination } from "@heroui/pagination"
1136
+
1137
+ <Pagination
1138
+ total={10}
1139
+ page={currentPage}
1140
+ onChange={setCurrentPage}
1141
+ />
1142
+ ```
1143
+
1144
+ **With boundary & siblings:**
1145
+ ```tsx
1146
+ <Pagination
1147
+ total={20}
1148
+ initialPage={1}
1149
+ boundaries={2}
1150
+ siblings={1}
1151
+ onChange={setPage}
1152
+ />
1153
+ ```
1154
+
1155
+ ---
1156
+
1157
+ ## Common Patterns
1158
+
1159
+ ### Form with Validation
1160
+
1161
+ **shadcn/ui:**
1162
+ ```tsx
1163
+ import { useForm } from "react-hook-form"
1164
+ import { zodResolver } from "@hookform/resolvers/zod"
1165
+ import * as z from "zod"
1166
+ import { Button } from "@/components/ui/button"
1167
+ import { Input } from "@/components/ui/input"
1168
+ import {
1169
+ Form,
1170
+ FormControl,
1171
+ FormField,
1172
+ FormItem,
1173
+ FormLabel,
1174
+ FormMessage,
1175
+ } from "@/components/ui/form"
1176
+
1177
+ const schema = z.object({
1178
+ email: z.string().email(),
1179
+ })
1180
+
1181
+ function MyForm() {
1182
+ const form = useForm({
1183
+ resolver: zodResolver(schema),
1184
+ })
1185
+
1186
+ return (
1187
+ <Form {...form}>
1188
+ <form onSubmit={form.handleSubmit(onSubmit)}>
1189
+ <FormField
1190
+ control={form.control}
1191
+ name="email"
1192
+ render={({ field }) => (
1193
+ <FormItem>
1194
+ <FormLabel>Email</FormLabel>
1195
+ <FormControl>
1196
+ <Input {...field} />
1197
+ </FormControl>
1198
+ <FormMessage />
1199
+ </FormItem>
1200
+ )}
1201
+ />
1202
+ <Button type="submit">Submit</Button>
1203
+ </form>
1204
+ </Form>
1205
+ )
1206
+ }
1207
+ ```
1208
+
1209
+ **HeroUI:**
1210
+ ```tsx
1211
+ import { useForm } from "react-hook-form"
1212
+ import { zodResolver } from "@hookform/resolvers/zod"
1213
+ import * as z from "zod"
1214
+ import { Button } from "@heroui/button"
1215
+ import { Input } from "@heroui/input"
1216
+
1217
+ const schema = z.object({
1218
+ email: z.string().email("Invalid email"),
1219
+ })
1220
+
1221
+ function MyForm() {
1222
+ const {
1223
+ register,
1224
+ handleSubmit,
1225
+ formState: { errors },
1226
+ } = useForm({
1227
+ resolver: zodResolver(schema),
1228
+ })
1229
+
1230
+ return (
1231
+ <form onSubmit={handleSubmit(onSubmit)} className="space-y-4">
1232
+ <Input
1233
+ {...register("email")}
1234
+ type="email"
1235
+ label="Email"
1236
+ variant="bordered"
1237
+ isInvalid={!!errors.email}
1238
+ errorMessage={errors.email?.message}
1239
+ />
1240
+ <Button type="submit" color="primary">
1241
+ Submit
1242
+ </Button>
1243
+ </form>
1244
+ )
1245
+ }
1246
+ ```
1247
+
1248
+ **Key Differences:**
1249
+ - No Form wrapper component needed
1250
+ - Simpler field registration
1251
+ - Built-in error display with `isInvalid` and `errorMessage`
1252
+
1253
+ ---
1254
+
1255
+ ### Loading States
1256
+
1257
+ **shadcn/ui:**
1258
+ ```tsx
1259
+ <Button disabled={isLoading}>
1260
+ {isLoading && <Loader2 className="mr-2 h-4 w-4 animate-spin" />}
1261
+ Submit
1262
+ </Button>
1263
+ ```
1264
+
1265
+ **HeroUI:**
1266
+ ```tsx
1267
+ <Button isLoading={isLoading}>
1268
+ Submit
1269
+ </Button>
1270
+ ```
1271
+
1272
+ Much cleaner! 🎉
1273
+
1274
+ ---
1275
+
1276
+ ### Conditional Rendering
1277
+
1278
+ **shadcn/ui:**
1279
+ ```tsx
1280
+ {isLoading ? (
1281
+ <Skeleton className="h-20 w-full" />
1282
+ ) : (
1283
+ <Card>Content</Card>
1284
+ )}
1285
+ ```
1286
+
1287
+ **HeroUI:**
1288
+ ```tsx
1289
+ <Skeleton isLoaded={!isLoading}>
1290
+ <Card>Content</Card>
1291
+ </Skeleton>
1292
+ ```
1293
+
1294
+ ---
1295
+
1296
+ ## Migration Checklist
1297
+
1298
+ Use this checklist when converting a page:
1299
+
1300
+ ### Before Starting
1301
+ - [ ] Read this migration guide
1302
+ - [ ] Identify all shadcn components used in the page
1303
+ - [ ] Check if HeroUI equivalent exists
1304
+
1305
+ ### During Migration
1306
+ - [ ] Replace imports
1307
+ - [ ] Update component names
1308
+ - [ ] Convert `className` to `classNames` where needed
1309
+ - [ ] Update variant prop names
1310
+ - [ ] Update boolean prop names (checked → isSelected, disabled → isDisabled)
1311
+ - [ ] Replace custom loading spinners with `isLoading` prop
1312
+ - [ ] Update form validation display
1313
+ - [ ] Test all interactive states
1314
+
1315
+ ### After Migration
1316
+ - [ ] Remove unused shadcn component files
1317
+ - [ ] Test all functionality
1318
+ - [ ] Check mobile responsiveness
1319
+ - [ ] Verify accessibility
1320
+ - [ ] Check loading states
1321
+ - [ ] Verify error states
1322
+ - [ ] Test form validation
1323
+
1324
+ ---
1325
+
1326
+ ## Quick Reference: Prop Name Changes
1327
+
1328
+ | shadcn/ui | HeroUI |
1329
+ |-----------|---------|
1330
+ | `className` | `classNames` (object) or `className` (string) |
1331
+ | `checked` | `isSelected` |
1332
+ | `disabled` | `isDisabled` |
1333
+ | `onCheckedChange` | `onValueChange` |
1334
+ | `onValueChange` (select) | `onChange` |
1335
+ | `open` | `isOpen` |
1336
+ | `onOpenChange` | `onClose` / `onOpenChange` |
1337
+ | `variant="default"` | `color="primary"` |
1338
+ | `variant="destructive"` | `color="danger"` |
1339
+ | `variant="outline"` | `variant="bordered"` |
1340
+
1341
+ ---
1342
+
1343
+ ## Tips
1344
+
1345
+ 1. **Start Simple:** Begin with simple components (Button, Input) before tackling complex ones
1346
+
1347
+ 2. **Use TypeScript:** HeroUI has excellent TypeScript support - use it!
1348
+
1349
+ 3. **Leverage Built-in States:** HeroUI components have built-in loading, disabled, invalid states
1350
+
1351
+ 4. **Consistent Variants:** Use `variant="bordered"` consistently for form inputs to match shadcn style
1352
+
1353
+ 5. **Check Examples:** See `UI_COMPONENTS_GUIDE.md` for complete HeroUI examples
1354
+
1355
+ 6. **Theme Customization:** If you need custom colors, use HeroUI's theme configuration
1356
+
1357
+ 7. **Accessibility:** HeroUI components are accessible by default - maintain this!
1358
+
1359
+ ---
1360
+
1361
+ ## Need Help?
1362
+
1363
+ - **HeroUI Docs:** https://www.heroui.com/docs
1364
+ - **This Project Examples:** Check `src/components/` folder for real implementations
1365
+ - **UI Components Guide:** See `UI_COMPONENTS_GUIDE.md` for detailed HeroUI patterns
1366
+
1367
+ ---
1368
+
1369
+ **Remember:** The goal is not just to replace components, but to leverage HeroUI's strengths:
1370
+ - Built-in states (loading, disabled, invalid)
1371
+ - Better TypeScript support
1372
+ - Consistent theming
1373
+ - Less custom code
1374
+
1375
+ Good luck with your migration! 🚀