@minhduydev/mdpi 0.4.0 → 0.5.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.
- package/dist/index.js +1 -1
- package/dist/template/.pi/VERSION +1 -1
- package/dist/template/.pi/extensions/templates-injector.ts +34 -6
- package/dist/template/.pi/prompts/INDEX.md +3 -9
- package/dist/template/.pi/skills/INDEX.md +81 -19
- package/dist/template/.pi/skills/accessibility-audit/SKILL.md +8 -2
- package/dist/template/.pi/skills/baseline-ui/SKILL.md +211 -0
- package/dist/template/.pi/skills/dcp-hygiene/SKILL.md +1 -1
- package/dist/template/.pi/skills/design-taste-frontend/SKILL.md +53 -42
- package/dist/template/.pi/skills/fixing-accessibility/SKILL.md +509 -0
- package/dist/template/.pi/skills/frontend-design/SKILL.md +60 -47
- package/dist/template/.pi/skills/frontend-design/references/animation/motion-advanced.md +88 -15
- package/dist/template/.pi/skills/frontend-design/references/animation/motion-core.md +148 -13
- package/dist/template/.pi/skills/frontend-design/references/shadcn/setup.md +127 -20
- package/dist/template/.pi/skills/frontend-ui-engineering/SKILL.md +21 -27
- package/dist/template/.pi/skills/nextjs-app-router/SKILL.md +334 -0
- package/dist/template/.pi/skills/nextjs-cache/SKILL.md +262 -0
- package/dist/template/.pi/skills/oklch-color-workflow/SKILL.md +426 -0
- package/dist/template/.pi/skills/production-hardening/SKILL.md +652 -0
- package/dist/template/.pi/skills/react-best-practices/SKILL.md +79 -1
- package/dist/template/.pi/skills/react-compiler/SKILL.md +237 -0
- package/dist/template/.pi/skills/react-hook-form/SKILL.md +374 -0
- package/dist/template/.pi/skills/react-server-actions/SKILL.md +299 -0
- package/dist/template/.pi/skills/shadcn-ui/SKILL.md +404 -0
- package/dist/template/.pi/skills/tanstack-query/SKILL.md +330 -0
- package/dist/template/.pi/skills/ui-craft-principles/SKILL.md +564 -0
- package/dist/template/.pi/skills/ui-quality-audit/SKILL.md +329 -0
- package/dist/template/.pi/skills/v0/SKILL.md +264 -0
- package/dist/template/.pi/skills/zustand/SKILL.md +333 -0
- package/dist/template/.pi/templates/DESIGN.md +76 -0
- package/dist/template/.pi/workflows/INDEX.md +2 -1
- package/dist/template/.pi/workflows/frontend-feature-workflow.md +343 -0
- package/dist/template/.pi/workflows/quality-loop.md +1 -1
- package/package.json +1 -1
- package/dist/template/.pi/prompts/loop-check.md +0 -87
- package/dist/template/.pi/prompts/loop-init.md +0 -157
- package/dist/template/.pi/prompts/loop-review.md +0 -90
- package/dist/template/.pi/skills/loop-audit/SKILL.md +0 -141
- package/dist/template/.pi/skills/loop-cost/SKILL.md +0 -130
- package/dist/template/.pi/skills/loop-engineering/SKILL.md +0 -175
- package/dist/template/.pi/templates/loop-github-action.yml +0 -162
- package/dist/template/.pi/templates/loop-orchestrator.sh +0 -514
- package/dist/template/.pi/templates/loop-orchestrator.test.ts +0 -332
- package/dist/template/.pi/templates/loop-orchestrator.ts +0 -936
- package/dist/template/.pi/templates/loop-state.json +0 -24
- package/dist/template/.pi/templates/loop-state.md +0 -98
- package/dist/template/.pi/templates/loop-vision.md +0 -110
- /package/dist/template/.pi/templates/{design.md → feature-design.md} +0 -0
|
@@ -0,0 +1,299 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: react-server-actions
|
|
3
|
+
description: Use when building forms, mutations, or data writes in React 19 + Next.js. Covers Server Actions, useActionState, useOptimistic, useFormStatus, progressive enhancement, Zod validation, error handling. MUST load before any form or mutation implementation.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# React Server Actions & Forms (React 19)
|
|
7
|
+
|
|
8
|
+
## When to Use
|
|
9
|
+
|
|
10
|
+
- Building forms that submit data to the server
|
|
11
|
+
- Handling mutations (create, update, delete) in React 19
|
|
12
|
+
- Adding optimistic updates to improve perceived performance
|
|
13
|
+
- Integrating Zod validation with Server Actions
|
|
14
|
+
- Migrating from API routes or tRPC to Server Actions
|
|
15
|
+
- Implementing progressive enhancement (forms work without JS)
|
|
16
|
+
|
|
17
|
+
## When NOT to Use
|
|
18
|
+
|
|
19
|
+
- Read-only data fetching (use Server Components, `use()`, or TanStack Query)
|
|
20
|
+
- Client-only state management (use Zustand or context)
|
|
21
|
+
- Non-Next.js React projects without Server Action support
|
|
22
|
+
|
|
23
|
+
## Core Pattern: Server Action + useActionState
|
|
24
|
+
|
|
25
|
+
```tsx
|
|
26
|
+
// app/actions.ts
|
|
27
|
+
'use server'
|
|
28
|
+
|
|
29
|
+
import { z } from 'zod'
|
|
30
|
+
import { revalidatePath } from 'next/cache'
|
|
31
|
+
|
|
32
|
+
const schema = z.object({
|
|
33
|
+
name: z.string().min(2),
|
|
34
|
+
email: z.string().email(),
|
|
35
|
+
})
|
|
36
|
+
|
|
37
|
+
export async function createUser(prevState: unknown, formData: FormData) {
|
|
38
|
+
// 1. Parse and validate
|
|
39
|
+
const raw = Object.fromEntries(formData)
|
|
40
|
+
const parsed = schema.safeParse(raw)
|
|
41
|
+
|
|
42
|
+
if (!parsed.success) {
|
|
43
|
+
return { error: parsed.error.flatten().fieldErrors }
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// 2. Mutate (database call)
|
|
47
|
+
await db.user.create({ data: parsed.data })
|
|
48
|
+
|
|
49
|
+
// 3. Revalidate and redirect
|
|
50
|
+
revalidatePath('/users')
|
|
51
|
+
return { success: true }
|
|
52
|
+
}
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
```tsx
|
|
56
|
+
// app/new/page.tsx
|
|
57
|
+
'use client'
|
|
58
|
+
|
|
59
|
+
import { useActionState } from 'react'
|
|
60
|
+
import { useFormStatus } from 'react-dom'
|
|
61
|
+
import { createUser } from './actions'
|
|
62
|
+
|
|
63
|
+
function SubmitButton() {
|
|
64
|
+
const { pending } = useFormStatus()
|
|
65
|
+
return (
|
|
66
|
+
<button type="submit" disabled={pending}>
|
|
67
|
+
{pending ? 'Creating...' : 'Create User'}
|
|
68
|
+
</button>
|
|
69
|
+
)
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
export default function NewUserForm() {
|
|
73
|
+
const [state, formAction] = useActionState(createUser, null)
|
|
74
|
+
|
|
75
|
+
return (
|
|
76
|
+
<form action={formAction}>
|
|
77
|
+
<input name="name" required />
|
|
78
|
+
{state?.error?.name && <p>{state.error.name[0]}</p>}
|
|
79
|
+
|
|
80
|
+
<input name="email" type="email" required />
|
|
81
|
+
{state?.error?.email && <p>{state.error.email[0]}</p>}
|
|
82
|
+
|
|
83
|
+
<SubmitButton />
|
|
84
|
+
{state?.success && <p className="text-green-600">User created!</p>}
|
|
85
|
+
</form>
|
|
86
|
+
)
|
|
87
|
+
}
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
## Hook Reference
|
|
91
|
+
|
|
92
|
+
### `useActionState(action, initialState, permalink?)`
|
|
93
|
+
|
|
94
|
+
```tsx
|
|
95
|
+
const [state, formAction, isPending] = useActionState(action, null)
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
- Replaces `useFormState` (deprecated in React 19)
|
|
99
|
+
- `state` — return value from your Server Action
|
|
100
|
+
- `formAction` — pass as `<form action={formAction}>`
|
|
101
|
+
- `isPending` — convenient boolean for loading state
|
|
102
|
+
- `permalink` — optional URL for progressive enhancement fallback
|
|
103
|
+
|
|
104
|
+
### `useFormStatus()`
|
|
105
|
+
|
|
106
|
+
```tsx
|
|
107
|
+
const { pending, data, method, action } = useFormStatus()
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
- **Must be used inside a `<form>` child component** — not in the component that renders `<form>`
|
|
111
|
+
- Extract `<SubmitButton>` as a separate component
|
|
112
|
+
- `pending` — true while form is submitting
|
|
113
|
+
- `data` — the FormData being submitted
|
|
114
|
+
|
|
115
|
+
### `useOptimistic(initialValue, reducer)`
|
|
116
|
+
|
|
117
|
+
```tsx
|
|
118
|
+
const [optimisticTodos, addOptimistic] = useOptimistic(
|
|
119
|
+
todos,
|
|
120
|
+
(state, newTodo: Todo) => [...state, newTodo]
|
|
121
|
+
)
|
|
122
|
+
|
|
123
|
+
// In event handler:
|
|
124
|
+
addOptimistic({ id: crypto.randomUUID(), text, pending: true })
|
|
125
|
+
await addTodoOnServer(formData)
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
- Shows UI change immediately, reverts on error
|
|
129
|
+
- `reducer` signature: `(currentState, optimisticValue) => newState`
|
|
130
|
+
- Good for: like counters, comment posting, toggle states
|
|
131
|
+
|
|
132
|
+
## Zod Integration
|
|
133
|
+
|
|
134
|
+
```tsx
|
|
135
|
+
'use server'
|
|
136
|
+
|
|
137
|
+
import { z } from 'zod'
|
|
138
|
+
|
|
139
|
+
const SignupSchema = z.object({
|
|
140
|
+
email: z.string().email('Invalid email'),
|
|
141
|
+
password: z.string().min(8, 'Min 8 characters'),
|
|
142
|
+
age: z.coerce.number().min(18, 'Must be 18+'),
|
|
143
|
+
plan: z.enum(['free', 'pro', 'enterprise']),
|
|
144
|
+
})
|
|
145
|
+
|
|
146
|
+
export async function signup(prev: unknown, formData: FormData) {
|
|
147
|
+
const result = SignupSchema.safeParse(Object.fromEntries(formData))
|
|
148
|
+
|
|
149
|
+
if (!result.success) {
|
|
150
|
+
// Return flattened errors keyed by field
|
|
151
|
+
return {
|
|
152
|
+
errors: result.error.flatten().fieldErrors,
|
|
153
|
+
inputs: Object.fromEntries(formData) // preserve user input
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
await createAccount(result.data)
|
|
158
|
+
return { success: true }
|
|
159
|
+
}
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
## Progressive Enhancement
|
|
163
|
+
|
|
164
|
+
Server Actions support HTML form fallback — forms work without JavaScript:
|
|
165
|
+
|
|
166
|
+
```tsx
|
|
167
|
+
// The form works even if JS fails to load:
|
|
168
|
+
<form action={createUser}>
|
|
169
|
+
<input name="name" required />
|
|
170
|
+
<button type="submit">Submit</button>
|
|
171
|
+
</form>
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
For the JS-enhanced version, use `useActionState` which wraps the same Server Action.
|
|
175
|
+
|
|
176
|
+
**Requirements for progressive enhancement:**
|
|
177
|
+
- Use native `<form>` and `<button type="submit">`
|
|
178
|
+
- Use `required` attribute for client-side validation
|
|
179
|
+
- All form fields must have `name` attributes
|
|
180
|
+
- Server Action must accept `FormData` as second argument
|
|
181
|
+
|
|
182
|
+
## Error Handling Pattern
|
|
183
|
+
|
|
184
|
+
```tsx
|
|
185
|
+
type ActionState = {
|
|
186
|
+
error?: string // General error
|
|
187
|
+
errors?: Record<string, string[]> // Field-level errors
|
|
188
|
+
success?: boolean // Success flag
|
|
189
|
+
data?: unknown // Return data on success
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
// In Server Action:
|
|
193
|
+
try {
|
|
194
|
+
await db.user.create({ data: parsed.data })
|
|
195
|
+
return { success: true }
|
|
196
|
+
} catch (err) {
|
|
197
|
+
if (err instanceof PrismaClientKnownRequestError && err.code === 'P2002') {
|
|
198
|
+
return { errors: { email: ['Email already registered'] } }
|
|
199
|
+
}
|
|
200
|
+
return { error: 'Something went wrong. Please try again.' }
|
|
201
|
+
}
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
## Redirect After Success
|
|
205
|
+
|
|
206
|
+
```tsx
|
|
207
|
+
'use server'
|
|
208
|
+
|
|
209
|
+
import { redirect } from 'next/navigation'
|
|
210
|
+
|
|
211
|
+
export async function createPost(prev: unknown, formData: FormData) {
|
|
212
|
+
const post = await db.post.create({ data: { title: formData.get('title') } })
|
|
213
|
+
revalidatePath('/posts')
|
|
214
|
+
redirect(`/posts/${post.id}`)
|
|
215
|
+
}
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
**Important**: `redirect()` throws a `NEXT_REDIRECT` error — call it after all mutations. Cannot be inside try/catch.
|
|
219
|
+
|
|
220
|
+
## Avoiding Common Pitfalls
|
|
221
|
+
|
|
222
|
+
| Pitfall | Fix |
|
|
223
|
+
|---------|-----|
|
|
224
|
+
| `useFormStatus()` in the form component itself | Extract `<SubmitButton>` to its own component |
|
|
225
|
+
| Not calling `revalidatePath` after mutation | Always revalidate the affected path |
|
|
226
|
+
| Using `redirect()` inside try/catch | Move redirect outside try/catch |
|
|
227
|
+
| Passing sensitive data as hidden inputs | Validate on server — never trust client data |
|
|
228
|
+
| Server Action not at top of file | `'use server'` directive must be first line |
|
|
229
|
+
| Mutating in Server Components | Server Components are read-only; use `'use client'` + action |
|
|
230
|
+
| Zod `safeParse` then ignoring errors | Always return errors to the form |
|
|
231
|
+
| Multiple forms on one page sharing action | Each form gets its own action or use a field to discriminate |
|
|
232
|
+
|
|
233
|
+
## Multiple Actions Per Form
|
|
234
|
+
|
|
235
|
+
```tsx
|
|
236
|
+
<form>
|
|
237
|
+
<button formAction={saveDraft}>Save Draft</button>
|
|
238
|
+
<button formAction={publish}>Publish</button>
|
|
239
|
+
</form>
|
|
240
|
+
```
|
|
241
|
+
|
|
242
|
+
Each button can have its own `formAction` pointing to a different Server Action.
|
|
243
|
+
|
|
244
|
+
## Non-Form Mutations (Calling Actions Programmatically)
|
|
245
|
+
|
|
246
|
+
```tsx
|
|
247
|
+
// For button clicks, toggles, etc. — import and call:
|
|
248
|
+
'use client'
|
|
249
|
+
|
|
250
|
+
import { toggleLike } from './actions'
|
|
251
|
+
|
|
252
|
+
function LikeButton({ postId }: { postId: string }) {
|
|
253
|
+
const [optimistic, addOptimistic] = useOptimistic(
|
|
254
|
+
false,
|
|
255
|
+
(_, liked: boolean) => liked
|
|
256
|
+
)
|
|
257
|
+
|
|
258
|
+
return (
|
|
259
|
+
<button
|
|
260
|
+
onClick={async () => {
|
|
261
|
+
addOptimistic(!optimistic)
|
|
262
|
+
await toggleLike(postId)
|
|
263
|
+
}}
|
|
264
|
+
>
|
|
265
|
+
{optimistic ? '❤️' : '🤍'}
|
|
266
|
+
</button>
|
|
267
|
+
)
|
|
268
|
+
}
|
|
269
|
+
```
|
|
270
|
+
|
|
271
|
+
## Integration with Other Skills
|
|
272
|
+
|
|
273
|
+
| Skill | Relationship |
|
|
274
|
+
|-------|-------------|
|
|
275
|
+
| `react-hook-form` | Alternative form approach (client-side state) — use when you need complex field interactions or field arrays |
|
|
276
|
+
| `nextjs-cache` | After mutation, `revalidatePath` / `revalidateTag` to invalidate cache |
|
|
277
|
+
| `nextjs-app-router` | Form pages use App Router conventions (loading.tsx for submit state) |
|
|
278
|
+
| `tanstack-query` | For GET/read operations — Server Actions are for mutations only |
|
|
279
|
+
|
|
280
|
+
## When to Use Server Actions vs API Routes
|
|
281
|
+
|
|
282
|
+
| Use Server Actions for | Use API Routes for |
|
|
283
|
+
|-----------------------|-------------------|
|
|
284
|
+
| Forms with progressive enhancement | Public APIs consumed by external clients |
|
|
285
|
+
| Mutations tightly coupled to UI | Webhooks / third-party callbacks |
|
|
286
|
+
| When you want co-located data flow | When you need cache headers, CORS, streaming |
|
|
287
|
+
| Optimistic updates | File uploads (use `multipart/form-data`) |
|
|
288
|
+
|
|
289
|
+
## Verification
|
|
290
|
+
|
|
291
|
+
- [ ] `'use server'` is the first line of the action file
|
|
292
|
+
- [ ] Server Action accepts `(prevState, formData)` matching `useActionState` signature
|
|
293
|
+
- [ ] `useFormStatus` is in a child component (not the form itself)
|
|
294
|
+
- [ ] All form fields have `name` attributes (for FormData extraction)
|
|
295
|
+
- [ ] Zod validation returns field-level errors
|
|
296
|
+
- [ ] `revalidatePath` / `revalidateTag` called after mutations
|
|
297
|
+
- [ ] `redirect()` outside try/catch blocks
|
|
298
|
+
- [ ] Progressive enhancement: form works with JS disabled
|
|
299
|
+
- [ ] Optimistic updates use `useOptimistic` with clean revert on error
|
|
@@ -0,0 +1,404 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: shadcn-ui
|
|
3
|
+
description: Use when adding, configuring, or managing shadcn/ui components. Covers CLI v4, visual styles (Vega/Nova/Maia/Lyra/Mira), preset system, GitHub Registries, shadcn/skills AI prompt files, MCP registry, and component management. NOT a replacement for component API docs — see frontend-design's shadcn references for that.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# shadcn/ui
|
|
7
|
+
|
|
8
|
+
## When to Use
|
|
9
|
+
|
|
10
|
+
- Adding shadcn/ui components to a project (`npx shadcn add`)
|
|
11
|
+
- Initializing shadcn/ui in a new project (`npx shadcn init` / `npx shadcn create`)
|
|
12
|
+
- Choosing a visual style (Vega, Nova, Maia, Lyra, Mira)
|
|
13
|
+
- Configuring via the preset system or `components.json`
|
|
14
|
+
- Using shadcn/skills for AI-driven component generation
|
|
15
|
+
- Inspecting installed components (`shadcn info --json`)
|
|
16
|
+
- Previewing component changes before applying (`--dry-run`, `--diff`, `--view`)
|
|
17
|
+
- Setting up custom component registries
|
|
18
|
+
|
|
19
|
+
## When NOT to Use
|
|
20
|
+
|
|
21
|
+
- When you need component API documentation (use `frontend-design` → `./references/shadcn/*.md`)
|
|
22
|
+
- When theming with CSS variables (use `frontend-design` → `./references/shadcn/theming.md`)
|
|
23
|
+
- When building plain UI without shadcn/ui components
|
|
24
|
+
- When the task is about v0 generation (use the `v0` skill instead)
|
|
25
|
+
|
|
26
|
+
## Relationship to `frontend-design` References
|
|
27
|
+
|
|
28
|
+
This skill covers **shadcn/ui tooling and workflow** — CLI commands, presets, registries, skills, and configuration management.
|
|
29
|
+
|
|
30
|
+
For **component API documentation** (Button props, Card subcomponents, Dialog patterns, Select usage), see:
|
|
31
|
+
- `frontend-design` → `./references/shadcn/setup.md` — Installation, visual styles, component list
|
|
32
|
+
- `frontend-design` → `./references/shadcn/core-components.md` — Button, Card, Dialog, Select, Tabs, Toast, Command, Sidebar, Table
|
|
33
|
+
- `frontend-design` → `./references/shadcn/form-components.md` — Form, Field, InputGroup
|
|
34
|
+
- `frontend-design` → `./references/shadcn/theming.md` — CSS variables, OKLCH, dark mode
|
|
35
|
+
- `frontend-design` → `./references/shadcn/accessibility.md` — ARIA, keyboard, screen reader
|
|
36
|
+
|
|
37
|
+
## CLI v4 (current: shadcn@4.11.0)
|
|
38
|
+
|
|
39
|
+
### Starting Projects
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
# Interactive project creation (recommended)
|
|
43
|
+
npx shadcn create
|
|
44
|
+
|
|
45
|
+
# This walks through:
|
|
46
|
+
# - Framework (Next.js, Vite, Remix, etc.)
|
|
47
|
+
# - Visual style (Vega, Nova, Maia, Lyra, Mira)
|
|
48
|
+
# - Component library backend (Radix UI or Base UI)
|
|
49
|
+
# - Icon library (including Phosphor)
|
|
50
|
+
# - TypeScript, Tailwind CSS, CSS variables setup
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
### Adding Components to Existing Projects
|
|
54
|
+
|
|
55
|
+
```bash
|
|
56
|
+
# Initialize shadcn in existing project
|
|
57
|
+
npx shadcn@latest init
|
|
58
|
+
|
|
59
|
+
# Add specific components
|
|
60
|
+
npx shadcn@latest add button card dialog
|
|
61
|
+
|
|
62
|
+
# Add all components
|
|
63
|
+
npx shadcn@latest add --all
|
|
64
|
+
|
|
65
|
+
# Add from GitHub Registry
|
|
66
|
+
npx shadcn@latest add <username>/<repo>/<item>
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
### Safety & Inspection
|
|
70
|
+
|
|
71
|
+
```bash
|
|
72
|
+
# Preview what will change (no writes)
|
|
73
|
+
npx shadcn@latest add button dialog --dry-run
|
|
74
|
+
|
|
75
|
+
# Show exact diff for a component
|
|
76
|
+
npx shadcn@latest add button --diff
|
|
77
|
+
|
|
78
|
+
# Open component in browser for review
|
|
79
|
+
npx shadcn@latest add button --view
|
|
80
|
+
|
|
81
|
+
# Inspect installed components as JSON
|
|
82
|
+
npx shadcn info --json
|
|
83
|
+
|
|
84
|
+
# Check version
|
|
85
|
+
npx shadcn --version
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
### `shadcn info --json`
|
|
89
|
+
|
|
90
|
+
Outputs structured data about installed components, useful for CI and tooling:
|
|
91
|
+
|
|
92
|
+
```json
|
|
93
|
+
{
|
|
94
|
+
"version": "4.11.0",
|
|
95
|
+
"components": [
|
|
96
|
+
{
|
|
97
|
+
"name": "button",
|
|
98
|
+
"installed": true,
|
|
99
|
+
"version": "4.11.0",
|
|
100
|
+
"path": "components/ui/button.tsx",
|
|
101
|
+
"dependencies": ["@radix-ui/react-slot"],
|
|
102
|
+
"registrySource": "default"
|
|
103
|
+
}
|
|
104
|
+
],
|
|
105
|
+
"style": "vega",
|
|
106
|
+
"aliases": {
|
|
107
|
+
"components": "@/components",
|
|
108
|
+
"ui": "@/components/ui",
|
|
109
|
+
"lib": "@/lib",
|
|
110
|
+
"utils": "@/lib/utils"
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
## Visual Styles
|
|
116
|
+
|
|
117
|
+
| Style | Vibe | Use Case |
|
|
118
|
+
|-------|------|----------|
|
|
119
|
+
| **Vega** | Classic shadcn — balanced, familiar | Default. Good for most projects. |
|
|
120
|
+
| **Nova** | Compact, reduced padding | Data-heavy dashboards, admin panels |
|
|
121
|
+
| **Maia** | Soft, rounded, generous padding | Consumer apps, marketing sites |
|
|
122
|
+
| **Lyra** | Boxy, sharp corners, monospace fonts | Developer tools, technical interfaces |
|
|
123
|
+
| **Mira** | Dense, efficient, minimal chrome | Internal tools, complex workflows |
|
|
124
|
+
|
|
125
|
+
Set during `npx shadcn create` or override per-component with `--style <name>`.
|
|
126
|
+
|
|
127
|
+
## Preset System
|
|
128
|
+
|
|
129
|
+
Presets pack your entire design system config into a reproducible short code:
|
|
130
|
+
|
|
131
|
+
```bash
|
|
132
|
+
# Export your design system as a preset code
|
|
133
|
+
npx shadcn preset export
|
|
134
|
+
|
|
135
|
+
# Init a new project with a preset
|
|
136
|
+
npx shadcn init --preset a1Dg5eFl
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
A preset captures: visual style (Vega/Nova/Maia/Lyra/Mira), component library (Radix/Base UI), colors, theme, icons, fonts, radius, spacing — everything needed to reproduce the exact design system.
|
|
140
|
+
|
|
141
|
+
## GitHub Registries (June 2026)
|
|
142
|
+
|
|
143
|
+
Any public GitHub repo with a `registry.json` can be a component registry.
|
|
144
|
+
|
|
145
|
+
```bash
|
|
146
|
+
# Pull from a GitHub registry
|
|
147
|
+
npx shadcn@latest add <username>/<repo>/<item>
|
|
148
|
+
|
|
149
|
+
# Example
|
|
150
|
+
npx shadcn@latest add shadcn-ui/ui/data-table
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
No build step needed — CLI reads `registry.json` directly. Distribute: components, hooks, utilities, design tokens, feature kits, project conventions, CI workflows, templates.
|
|
154
|
+
|
|
155
|
+
### Registry Structure
|
|
156
|
+
|
|
157
|
+
```json
|
|
158
|
+
// registry.json (in any GitHub repo root)
|
|
159
|
+
{
|
|
160
|
+
"name": "my-components",
|
|
161
|
+
"items": [
|
|
162
|
+
{
|
|
163
|
+
"name": "data-table",
|
|
164
|
+
"type": "registry:component",
|
|
165
|
+
"files": [{ "path": "components/data-table.tsx" }],
|
|
166
|
+
"dependencies": ["@tanstack/react-table"]
|
|
167
|
+
}
|
|
168
|
+
]
|
|
169
|
+
}
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
## MCP Registry Server
|
|
173
|
+
|
|
174
|
+
Connect AI editors to your custom shadcn/ui registry:
|
|
175
|
+
|
|
176
|
+
```json
|
|
177
|
+
{
|
|
178
|
+
"mcpServers": {
|
|
179
|
+
"shadcn": {
|
|
180
|
+
"command": "npx",
|
|
181
|
+
"args": ["-y", "shadcn@canary", "registry:mcp"],
|
|
182
|
+
"env": {
|
|
183
|
+
"REGISTRY_URL": "https://your-registry.vercel.app/r/registry.json"
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
This allows AI agents (Cursor, Windsurf, Claude Desktop) to query and pull components from your custom design system registry.
|
|
191
|
+
|
|
192
|
+
## shadcn/skills — AI Agent Prompt File
|
|
193
|
+
|
|
194
|
+
A machine-readable skills file that gives AI coding agents accurate, project-aware context about shadcn/ui.
|
|
195
|
+
|
|
196
|
+
### Setup
|
|
197
|
+
|
|
198
|
+
```bash
|
|
199
|
+
npx skills add shadcn/ui
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
Creates `.shadcn/skills.md` — automatically injected into AI agent context.
|
|
203
|
+
|
|
204
|
+
### What It Provides
|
|
205
|
+
|
|
206
|
+
- **Project context** — framework, aliases, installed components (via `shadcn info --json`)
|
|
207
|
+
- **CLI commands** — all flags, smart merge, presets, templates
|
|
208
|
+
- **Theming** — CSS vars, OKLCH, dark mode, Tailwind v3/v4
|
|
209
|
+
- **Registry authoring** — `registry.json` format, item types, dependencies
|
|
210
|
+
- **Pattern enforcement** — `FieldGroup` for forms, `ToggleGroup` for options, semantic colors
|
|
211
|
+
|
|
212
|
+
### Measured Impact
|
|
213
|
+
|
|
214
|
+
| Metric | Before Skills | After Skills |
|
|
215
|
+
|--------|--------------|-------------|
|
|
216
|
+
| API errors | 34% | 3% |
|
|
217
|
+
| Correct variants | 61% | 98% |
|
|
218
|
+
| First-try success | 45% | 89% |
|
|
219
|
+
|
|
220
|
+
### Critical Workflows
|
|
221
|
+
|
|
222
|
+
```bash
|
|
223
|
+
# Install
|
|
224
|
+
npx skills add shadcn/ui
|
|
225
|
+
|
|
226
|
+
# Regenerate after adding components (CRITICAL!)
|
|
227
|
+
npx shadcn skills generate
|
|
228
|
+
|
|
229
|
+
# Update monthly to stay current
|
|
230
|
+
npx shadcn skills update
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
### Pitfalls
|
|
234
|
+
|
|
235
|
+
| Mistake | Fix |
|
|
236
|
+
|---------|-----|
|
|
237
|
+
| Not regenerating after adding components | Add `shadcn skills generate` to post-install script |
|
|
238
|
+
| Skills getting cut off from context limit | Reference file path; don't paste contents |
|
|
239
|
+
| Using outdated skills (3+ months old) | Run `shadcn skills update` monthly |
|
|
240
|
+
| Ignoring the preset system | Use presets; customize through Tailwind config, not per-component CSS |
|
|
241
|
+
|
|
242
|
+
## Components (2026)
|
|
243
|
+
|
|
244
|
+
### New in 2026
|
|
245
|
+
|
|
246
|
+
| Component | Description |
|
|
247
|
+
|-----------|-------------|
|
|
248
|
+
| **Field** | Form field wrapper with label, description, error message |
|
|
249
|
+
| **InputGroup** | Grouped inputs with addons (prefix/suffix) |
|
|
250
|
+
| **Spinner** | Loading spinner with size variants |
|
|
251
|
+
| **Kbd** | Keyboard shortcut display |
|
|
252
|
+
| **ButtonGroup** | Grouped button toolbar |
|
|
253
|
+
| **Item** | Generic list item with icon, text, and actions |
|
|
254
|
+
| **Empty** | Empty state with illustration, text, and action |
|
|
255
|
+
|
|
256
|
+
### Component Installation
|
|
257
|
+
|
|
258
|
+
```bash
|
|
259
|
+
npx shadcn@latest add field input-group spinner kbd button-group item empty
|
|
260
|
+
```
|
|
261
|
+
|
|
262
|
+
### Quick Reference — New Components
|
|
263
|
+
|
|
264
|
+
#### Field
|
|
265
|
+
|
|
266
|
+
```tsx
|
|
267
|
+
import { Field, FieldLabel, FieldDescription, FieldError } from "@/components/ui/field"
|
|
268
|
+
|
|
269
|
+
<Field>
|
|
270
|
+
<FieldLabel>Email</FieldLabel>
|
|
271
|
+
<Input placeholder="you@example.com" />
|
|
272
|
+
<FieldDescription>We'll never share your email.</FieldDescription>
|
|
273
|
+
<FieldError>{errors.email}</FieldError>
|
|
274
|
+
</Field>
|
|
275
|
+
```
|
|
276
|
+
|
|
277
|
+
#### InputGroup
|
|
278
|
+
|
|
279
|
+
```tsx
|
|
280
|
+
import { InputGroup, InputGroupAddon } from "@/components/ui/input-group"
|
|
281
|
+
|
|
282
|
+
<InputGroup>
|
|
283
|
+
<InputGroupAddon>https://</InputGroupAddon>
|
|
284
|
+
<Input placeholder="example.com" />
|
|
285
|
+
<InputGroupAddon>.com</InputGroupAddon>
|
|
286
|
+
</InputGroup>
|
|
287
|
+
```
|
|
288
|
+
|
|
289
|
+
#### Spinner
|
|
290
|
+
|
|
291
|
+
```tsx
|
|
292
|
+
import { Spinner } from "@/components/ui/spinner"
|
|
293
|
+
|
|
294
|
+
<Spinner />
|
|
295
|
+
<Spinner size="sm" />
|
|
296
|
+
<Spinner className="text-primary" />
|
|
297
|
+
```
|
|
298
|
+
|
|
299
|
+
#### Kbd
|
|
300
|
+
|
|
301
|
+
```tsx
|
|
302
|
+
import { Kbd } from "@/components/ui/kbd"
|
|
303
|
+
|
|
304
|
+
<Kbd>⌘K</Kbd>
|
|
305
|
+
<Kbd variant="outline">Ctrl + S</Kbd>
|
|
306
|
+
```
|
|
307
|
+
|
|
308
|
+
#### ButtonGroup
|
|
309
|
+
|
|
310
|
+
```tsx
|
|
311
|
+
import { ButtonGroup } from "@/components/ui/button-group"
|
|
312
|
+
import { Button } from "@/components/ui/button"
|
|
313
|
+
|
|
314
|
+
<ButtonGroup>
|
|
315
|
+
<Button variant="outline">Day</Button>
|
|
316
|
+
<Button variant="outline">Week</Button>
|
|
317
|
+
<Button variant="outline">Month</Button>
|
|
318
|
+
</ButtonGroup>
|
|
319
|
+
```
|
|
320
|
+
|
|
321
|
+
#### Item
|
|
322
|
+
|
|
323
|
+
```tsx
|
|
324
|
+
import { Item, ItemIcon, ItemText, ItemAction } from "@/components/ui/item"
|
|
325
|
+
|
|
326
|
+
<Item>
|
|
327
|
+
<ItemIcon><SettingsIcon /></ItemIcon>
|
|
328
|
+
<ItemText primary="Settings" secondary="Manage preferences" />
|
|
329
|
+
<ItemAction><ChevronRight /></ItemAction>
|
|
330
|
+
</Item>
|
|
331
|
+
```
|
|
332
|
+
|
|
333
|
+
#### Empty
|
|
334
|
+
|
|
335
|
+
```tsx
|
|
336
|
+
import { Empty } from "@/components/ui/empty"
|
|
337
|
+
|
|
338
|
+
<Empty
|
|
339
|
+
title="No results"
|
|
340
|
+
description="Try adjusting your filters."
|
|
341
|
+
action={<Button>Clear filters</Button>}
|
|
342
|
+
/>
|
|
343
|
+
```
|
|
344
|
+
|
|
345
|
+
## Configuration
|
|
346
|
+
|
|
347
|
+
### `components.json`
|
|
348
|
+
|
|
349
|
+
The central configuration file:
|
|
350
|
+
|
|
351
|
+
```json
|
|
352
|
+
{
|
|
353
|
+
"$schema": "https://ui.shadcn.com/schema.json",
|
|
354
|
+
"style": "nova",
|
|
355
|
+
"rsc": true,
|
|
356
|
+
"tsx": true,
|
|
357
|
+
"tailwind": {
|
|
358
|
+
"config": "tailwind.config.ts",
|
|
359
|
+
"css": "app/globals.css",
|
|
360
|
+
"cssVariables": true,
|
|
361
|
+
"prefix": ""
|
|
362
|
+
},
|
|
363
|
+
"aliases": {
|
|
364
|
+
"components": "@/components",
|
|
365
|
+
"ui": "@/components/ui",
|
|
366
|
+
"lib": "@/lib",
|
|
367
|
+
"utils": "@/lib/utils",
|
|
368
|
+
"hooks": "@/hooks"
|
|
369
|
+
},
|
|
370
|
+
"iconLibrary": "lucide",
|
|
371
|
+
"registries": [
|
|
372
|
+
{
|
|
373
|
+
"name": "team",
|
|
374
|
+
"url": "gh:my-org/shadcn-registry"
|
|
375
|
+
}
|
|
376
|
+
]
|
|
377
|
+
}
|
|
378
|
+
```
|
|
379
|
+
|
|
380
|
+
## Anti-Patterns
|
|
381
|
+
|
|
382
|
+
| Anti-Pattern | Why | Correct |
|
|
383
|
+
|---|---|---|
|
|
384
|
+
| Manually editing component primitives | Upstream updates become impossible to merge | Extend with wrapper components instead |
|
|
385
|
+
| Importing from shadcn as a package | shadcn/ui is copy-paste, not an npm package | Components live in your `components/ui/` directory |
|
|
386
|
+
| Adding `--all` without reviewing | Installs 50+ components, most unused | Only install what you need |
|
|
387
|
+
| Using hard-coded Tailwind values | Breaks theming and dark mode | Reference CSS variable tokens |
|
|
388
|
+
| Overriding `@theme` without preserving shadcn tokens | Breaks component styling | Extend `@theme`, don't replace it |
|
|
389
|
+
| Editing CSS variables inline | Hard to maintain theme switching | Define variables once, reference by semantic name |
|
|
390
|
+
|
|
391
|
+
## Verification
|
|
392
|
+
|
|
393
|
+
After working with shadcn/ui:
|
|
394
|
+
|
|
395
|
+
- [ ] `components.json` exists and is configured correctly
|
|
396
|
+
- [ ] Only needed components are installed (no `--all` waste)
|
|
397
|
+
- [ ] CSS variables are used for colors (no hard-coded hex/rgb in components)
|
|
398
|
+
- [ ] Dark mode works via CSS variable overrides
|
|
399
|
+
- [ ] Components compile with TypeScript strict mode
|
|
400
|
+
- [ ] `npx shadcn info --json` shows expected installed components
|
|
401
|
+
- [ ] Preset saved if project has custom styling
|
|
402
|
+
- [ ] `shadcn/skills/` directory exists if project has custom conventions
|
|
403
|
+
- [ ] Aliases match project structure (`@/components/ui/`, `@/lib/utils`)
|
|
404
|
+
- [ ] No manually edited primitives that would conflict with updates
|