@minhduydev/mdpi 0.4.1 → 0.6.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 +4 -2
- package/dist/template/.pi/AGENTS.md +1 -1
- package/dist/template/.pi/README.md +2 -3
- package/dist/template/.pi/VERSION +1 -1
- package/dist/template/.pi/agents/explore.md +1 -1
- package/dist/template/.pi/agents/scout.md +1 -1
- package/dist/template/.pi/extensions/templates-injector.ts +35 -7
- package/dist/template/.pi/prompts/INDEX.md +3 -9
- package/dist/template/.pi/prompts/gc.md +2 -1
- package/dist/template/.pi/prompts/verify.md +24 -0
- package/dist/template/.pi/skills/INDEX.md +40 -8
- package/dist/template/.pi/skills/dcp-hygiene/SKILL.md +1 -1
- package/dist/template/.pi/skills/frontend-design/SKILL.md +1 -1
- 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/nextjs-app-router/SKILL.md +334 -0
- package/dist/template/.pi/skills/nextjs-cache/SKILL.md +262 -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/v0/SKILL.md +264 -0
- package/dist/template/.pi/skills/zustand/SKILL.md +333 -0
- package/package.json +1 -1
- package/dist/template/.pi/context/fallow.md +0 -137
- 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
|
@@ -0,0 +1,330 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: tanstack-query
|
|
3
|
+
description: Use when implementing data fetching, caching, or mutations with TanStack Query v5. Covers useQuery, useMutation, optimistic updates, infinite queries, prefetching, SSR patterns, query keys. MUST load before any data fetching implementation.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# TanStack Query v5
|
|
7
|
+
|
|
8
|
+
## When to Use
|
|
9
|
+
|
|
10
|
+
- Fetching data from APIs in React applications
|
|
11
|
+
- Managing server state with automatic caching and background refetching
|
|
12
|
+
- Implementing optimistic updates for mutations
|
|
13
|
+
- Handling pagination and infinite scrolling
|
|
14
|
+
- Prefetching data for faster navigation
|
|
15
|
+
- Server-side rendering with client hydration
|
|
16
|
+
|
|
17
|
+
## When NOT to Use
|
|
18
|
+
|
|
19
|
+
- Server Components with direct DB access (use `use cache` instead)
|
|
20
|
+
- WebSocket-only real-time data (use SWR subscription or custom hook)
|
|
21
|
+
- Form state management (use React Hook Form)
|
|
22
|
+
- Global client-only state (use Zustand)
|
|
23
|
+
|
|
24
|
+
## Setup
|
|
25
|
+
|
|
26
|
+
```tsx
|
|
27
|
+
// app/providers.tsx
|
|
28
|
+
'use client'
|
|
29
|
+
|
|
30
|
+
import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
|
|
31
|
+
import { useState } from 'react'
|
|
32
|
+
|
|
33
|
+
export function Providers({ children }: { children: React.ReactNode }) {
|
|
34
|
+
const [queryClient] = useState(
|
|
35
|
+
() => new QueryClient({
|
|
36
|
+
defaultOptions: {
|
|
37
|
+
queries: {
|
|
38
|
+
staleTime: 60 * 1000, // 1 min before considered stale
|
|
39
|
+
gcTime: 5 * 60 * 1000, // 5 min garbage collection
|
|
40
|
+
retry: 1, // One retry on failure
|
|
41
|
+
refetchOnWindowFocus: false,
|
|
42
|
+
},
|
|
43
|
+
},
|
|
44
|
+
})
|
|
45
|
+
)
|
|
46
|
+
|
|
47
|
+
return (
|
|
48
|
+
<QueryClientProvider client={queryClient}>
|
|
49
|
+
{children}
|
|
50
|
+
</QueryClientProvider>
|
|
51
|
+
)
|
|
52
|
+
}
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
```tsx
|
|
56
|
+
// app/layout.tsx
|
|
57
|
+
import { Providers } from './providers'
|
|
58
|
+
|
|
59
|
+
export default function RootLayout({ children }) {
|
|
60
|
+
return (
|
|
61
|
+
<html>
|
|
62
|
+
<body>
|
|
63
|
+
<Providers>{children}</Providers>
|
|
64
|
+
</body>
|
|
65
|
+
</html>
|
|
66
|
+
)
|
|
67
|
+
}
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
## useQuery — Basic Data Fetching
|
|
71
|
+
|
|
72
|
+
```tsx
|
|
73
|
+
'use client'
|
|
74
|
+
|
|
75
|
+
import { useQuery } from '@tanstack/react-query'
|
|
76
|
+
|
|
77
|
+
function PostsList() {
|
|
78
|
+
const { data, isLoading, error } = useQuery({
|
|
79
|
+
queryKey: ['posts'],
|
|
80
|
+
queryFn: () => fetch('/api/posts').then(res => res.json()),
|
|
81
|
+
})
|
|
82
|
+
|
|
83
|
+
if (isLoading) return <PostsSkeleton />
|
|
84
|
+
if (error) return <ErrorDisplay error={error} />
|
|
85
|
+
|
|
86
|
+
return data.map(post => <PostCard key={post.id} post={post} />)
|
|
87
|
+
}
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
## Query Key Design
|
|
91
|
+
|
|
92
|
+
```tsx
|
|
93
|
+
// Flat keys — simple
|
|
94
|
+
useQuery({ queryKey: ['posts'], queryFn: fetchPosts })
|
|
95
|
+
|
|
96
|
+
// Hierarchical keys — filterable
|
|
97
|
+
useQuery({ queryKey: ['posts', { status: 'published' }], queryFn: () => fetchPosts('published') })
|
|
98
|
+
|
|
99
|
+
// Detail queries — id-based
|
|
100
|
+
useQuery({ queryKey: ['posts', postId], queryFn: () => fetchPost(postId) })
|
|
101
|
+
|
|
102
|
+
// Factory pattern — recommended for large apps
|
|
103
|
+
const postKeys = {
|
|
104
|
+
all: ['posts'] as const,
|
|
105
|
+
lists: () => [...postKeys.all, 'list'] as const,
|
|
106
|
+
list: (filters: Filters) => [...postKeys.lists(), filters] as const,
|
|
107
|
+
details: () => [...postKeys.all, 'detail'] as const,
|
|
108
|
+
detail: (id: string) => [...postKeys.details(), id] as const,
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// Usage:
|
|
112
|
+
useQuery({ queryKey: postKeys.lists({ status: 'published' }), ... })
|
|
113
|
+
useQuery({ queryKey: postKeys.detail(id), ... })
|
|
114
|
+
|
|
115
|
+
// Invalidation
|
|
116
|
+
queryClient.invalidateQueries({ queryKey: postKeys.lists() }) // All lists
|
|
117
|
+
queryClient.invalidateQueries({ queryKey: postKeys.detail(id) }) // Specific post
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
## useMutation
|
|
121
|
+
|
|
122
|
+
```tsx
|
|
123
|
+
import { useMutation, useQueryClient } from '@tanstack/react-query'
|
|
124
|
+
|
|
125
|
+
function CreatePost() {
|
|
126
|
+
const queryClient = useQueryClient()
|
|
127
|
+
|
|
128
|
+
const mutation = useMutation({
|
|
129
|
+
mutationFn: (newPost: PostInput) =>
|
|
130
|
+
fetch('/api/posts', {
|
|
131
|
+
method: 'POST',
|
|
132
|
+
body: JSON.stringify(newPost),
|
|
133
|
+
}),
|
|
134
|
+
|
|
135
|
+
// Invalidate and refetch after success
|
|
136
|
+
onSuccess: () => {
|
|
137
|
+
queryClient.invalidateQueries({ queryKey: ['posts'] })
|
|
138
|
+
},
|
|
139
|
+
})
|
|
140
|
+
|
|
141
|
+
return (
|
|
142
|
+
<button
|
|
143
|
+
disabled={mutation.isPending}
|
|
144
|
+
onClick={() => mutation.mutate({ title: 'New Post' })}
|
|
145
|
+
>
|
|
146
|
+
{mutation.isPending ? 'Creating...' : 'Create Post'}
|
|
147
|
+
</button>
|
|
148
|
+
)
|
|
149
|
+
}
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
## Optimistic Updates
|
|
153
|
+
|
|
154
|
+
```tsx
|
|
155
|
+
const mutation = useMutation({
|
|
156
|
+
mutationFn: toggleTodoStatus,
|
|
157
|
+
|
|
158
|
+
onMutate: async (todoId) => {
|
|
159
|
+
// Cancel outgoing refetches
|
|
160
|
+
await queryClient.cancelQueries({ queryKey: ['todos'] })
|
|
161
|
+
|
|
162
|
+
// Snapshot previous value
|
|
163
|
+
const previousTodos = queryClient.getQueryData(['todos'])
|
|
164
|
+
|
|
165
|
+
// Optimistically update
|
|
166
|
+
queryClient.setQueryData(['todos'], (old: Todo[]) =>
|
|
167
|
+
old.map(t => t.id === todoId ? { ...t, done: !t.done } : t)
|
|
168
|
+
)
|
|
169
|
+
|
|
170
|
+
// Return context for rollback
|
|
171
|
+
return { previousTodos }
|
|
172
|
+
},
|
|
173
|
+
|
|
174
|
+
onError: (err, todoId, context) => {
|
|
175
|
+
// Rollback on failure
|
|
176
|
+
queryClient.setQueryData(['todos'], context?.previousTodos)
|
|
177
|
+
},
|
|
178
|
+
|
|
179
|
+
onSettled: () => {
|
|
180
|
+
// Refetch to sync with server
|
|
181
|
+
queryClient.invalidateQueries({ queryKey: ['todos'] })
|
|
182
|
+
},
|
|
183
|
+
})
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
## Infinite Queries
|
|
187
|
+
|
|
188
|
+
```tsx
|
|
189
|
+
import { useInfiniteQuery } from '@tanstack/react-query'
|
|
190
|
+
|
|
191
|
+
function PostFeed() {
|
|
192
|
+
const {
|
|
193
|
+
data,
|
|
194
|
+
fetchNextPage,
|
|
195
|
+
hasNextPage,
|
|
196
|
+
isFetchingNextPage,
|
|
197
|
+
} = useInfiniteQuery({
|
|
198
|
+
queryKey: ['posts', 'infinite'],
|
|
199
|
+
queryFn: ({ pageParam }) =>
|
|
200
|
+
fetch(`/api/posts?cursor=${pageParam}`).then(r => r.json()),
|
|
201
|
+
initialPageParam: 0,
|
|
202
|
+
getNextPageParam: (lastPage) => lastPage.nextCursor ?? undefined,
|
|
203
|
+
})
|
|
204
|
+
|
|
205
|
+
return (
|
|
206
|
+
<div>
|
|
207
|
+
{data.pages.map(page =>
|
|
208
|
+
page.posts.map(post => <PostCard key={post.id} post={post} />)
|
|
209
|
+
)}
|
|
210
|
+
{hasNextPage && (
|
|
211
|
+
<button onClick={() => fetchNextPage()} disabled={isFetchingNextPage}>
|
|
212
|
+
Load more
|
|
213
|
+
</button>
|
|
214
|
+
)}
|
|
215
|
+
</div>
|
|
216
|
+
)
|
|
217
|
+
}
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
## Prefetching (Next.js)
|
|
221
|
+
|
|
222
|
+
```tsx
|
|
223
|
+
// app/posts/page.tsx — Server Component
|
|
224
|
+
import { dehydrate, HydrationBoundary, QueryClient } from '@tanstack/react-query'
|
|
225
|
+
import { PostsList } from './PostsList'
|
|
226
|
+
|
|
227
|
+
export default async function PostsPage() {
|
|
228
|
+
const queryClient = new QueryClient()
|
|
229
|
+
|
|
230
|
+
// Prefetch on server
|
|
231
|
+
await queryClient.prefetchQuery({
|
|
232
|
+
queryKey: ['posts'],
|
|
233
|
+
queryFn: fetchPosts,
|
|
234
|
+
})
|
|
235
|
+
|
|
236
|
+
return (
|
|
237
|
+
<HydrationBoundary state={dehydrate(queryClient)}>
|
|
238
|
+
<PostsList />
|
|
239
|
+
</HydrationBoundary>
|
|
240
|
+
)
|
|
241
|
+
}
|
|
242
|
+
```
|
|
243
|
+
|
|
244
|
+
```tsx
|
|
245
|
+
// app/posts/PostsList.tsx — Client Component
|
|
246
|
+
'use client'
|
|
247
|
+
|
|
248
|
+
import { useQuery } from '@tanstack/react-query'
|
|
249
|
+
|
|
250
|
+
export function PostsList() {
|
|
251
|
+
// Uses prefetched data — no loading state on first render
|
|
252
|
+
const { data } = useQuery({ queryKey: ['posts'], queryFn: fetchPosts })
|
|
253
|
+
return data.map(post => <PostCard key={post.id} post={post} />)
|
|
254
|
+
}
|
|
255
|
+
```
|
|
256
|
+
|
|
257
|
+
## Combining with Server Actions
|
|
258
|
+
|
|
259
|
+
```tsx
|
|
260
|
+
// Use Server Actions for mutations, TanStack Query for reads:
|
|
261
|
+
|
|
262
|
+
'use client'
|
|
263
|
+
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'
|
|
264
|
+
import { createPost } from './actions' // Server Action
|
|
265
|
+
|
|
266
|
+
function PostsPage() {
|
|
267
|
+
const queryClient = useQueryClient()
|
|
268
|
+
|
|
269
|
+
// Read — TanStack Query
|
|
270
|
+
const posts = useQuery({
|
|
271
|
+
queryKey: ['posts'],
|
|
272
|
+
queryFn: () => fetch('/api/posts').then(r => r.json()),
|
|
273
|
+
})
|
|
274
|
+
|
|
275
|
+
// Write — Server Action + cache revalidation
|
|
276
|
+
const mutation = useMutation({
|
|
277
|
+
mutationFn: (data: FormData) => createPost(null, data),
|
|
278
|
+
onSuccess: () => queryClient.invalidateQueries({ queryKey: ['posts'] }),
|
|
279
|
+
})
|
|
280
|
+
|
|
281
|
+
return (
|
|
282
|
+
<form action={mutation.mutate}>
|
|
283
|
+
<input name="title" />
|
|
284
|
+
<button>Create</button>
|
|
285
|
+
</form>
|
|
286
|
+
)
|
|
287
|
+
}
|
|
288
|
+
```
|
|
289
|
+
|
|
290
|
+
## staleTime vs gcTime
|
|
291
|
+
|
|
292
|
+
| Setting | What it controls | Recommended |
|
|
293
|
+
|---------|-----------------|-------------|
|
|
294
|
+
| `staleTime` | How long before data is considered stale and refetched | 30s–5min depending on update frequency |
|
|
295
|
+
| `gcTime` | How long inactive data stays in cache before garbage collection | 5–30min (longer than staleTime) |
|
|
296
|
+
|
|
297
|
+
```tsx
|
|
298
|
+
// Static data — rarely changes
|
|
299
|
+
staleTime: Infinity, gcTime: 30 * 60 * 1000,
|
|
300
|
+
|
|
301
|
+
// Dashboard — updates every few minutes
|
|
302
|
+
staleTime: 5 * 60 * 1000, gcTime: 30 * 60 * 1000,
|
|
303
|
+
|
|
304
|
+
// Real-time feed — updates frequently
|
|
305
|
+
staleTime: 30 * 1000, gcTime: 5 * 60 * 1000,
|
|
306
|
+
```
|
|
307
|
+
|
|
308
|
+
## Common Pitfalls
|
|
309
|
+
|
|
310
|
+
| Pitfall | Fix |
|
|
311
|
+
|---------|-----|
|
|
312
|
+
| Using TanStack Query in Server Components | Move to `'use client'` component |
|
|
313
|
+
| `queryKey` as `['posts']` everywhere — no granularity | Use structured keys: `['posts', 'list', filters]` |
|
|
314
|
+
| Not setting `staleTime` — defaults to 0 | Set sensible `staleTime` — default 0 refetches too often |
|
|
315
|
+
| Mixing `isLoading` and `isFetching` | `isLoading` = first load; `isFetching` = any fetch including background |
|
|
316
|
+
| `useQuery` for mutations | `useQuery` is for reads; use `useMutation` for writes |
|
|
317
|
+
| Mutating cache directly without rollback | Always implement `onMutate` snapshot + `onError` rollback |
|
|
318
|
+
| Missing `HydrationBoundary` for SSR | Server-prefetched data won't hydrate without it |
|
|
319
|
+
| `queryClient` recreated every render | Wrap in `useState(() => new QueryClient(...))` |
|
|
320
|
+
|
|
321
|
+
## Verification
|
|
322
|
+
|
|
323
|
+
- [ ] `QueryClientProvider` wraps the app in root layout
|
|
324
|
+
- [ ] `staleTime` and `gcTime` configured globally (not per-query unless needed)
|
|
325
|
+
- [ ] Query keys follow a structured pattern (all → lists → detail)
|
|
326
|
+
- [ ] Mutations call `invalidateQueries` or use optimistic updates
|
|
327
|
+
- [ ] Optimistic updates have rollback via `onError` + `onMutate` snapshot
|
|
328
|
+
- [ ] SSR pages use `HydrationBoundary` with `dehydrate`
|
|
329
|
+
- [ ] No `useQuery` in Server Components
|
|
330
|
+
- [ ] `isLoading` used for first-load skeleton; `isFetching` for background updates
|
|
@@ -0,0 +1,264 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: v0
|
|
3
|
+
description: Use when generating UI with v0 (Vercel's AI-powered generator). Covers prompt engineering, CLI/SDK/MCP integration, shadcn/ui-aware patterns. MUST load before any v0 workflow.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# v0 — Vercel AI UI Generator
|
|
7
|
+
|
|
8
|
+
## Overview
|
|
9
|
+
|
|
10
|
+
v0 (v0.app) is Vercel's AI-powered UI and application generator. It generates React components using **shadcn/ui** primitives + **Tailwind CSS**, targeting **Next.js App Router** by default.
|
|
11
|
+
|
|
12
|
+
In February 2026, v0 evolved to **v2.0** — a full production development platform. Beyond UI generation, it now supports: importing GitHub repos into sandboxes, git branching/PRs/merging from the browser, database integrations (Snowflake, AWS DBs), enterprise SSO, and agent-based workflows.
|
|
13
|
+
|
|
14
|
+
v0 generates "code that looks like a senior frontend engineer wrote it."
|
|
15
|
+
|
|
16
|
+
## When to Use
|
|
17
|
+
|
|
18
|
+
- Generating shadcn/ui components or pages with AI
|
|
19
|
+
- Writing effective v0 prompts using the 5-block brief
|
|
20
|
+
- Adding v0-generated code to projects via CLI or SDK
|
|
21
|
+
- Integrating v0 with IDEs via MCP (`https://mcp.v0.dev`)
|
|
22
|
+
- Building programmatic UI generation workflows with the v0 SDK
|
|
23
|
+
- Designing brand-aware v0 output via custom shadcn/ui registries
|
|
24
|
+
|
|
25
|
+
## When NOT to Use
|
|
26
|
+
|
|
27
|
+
- Plain UI without AI generation (use `frontend-design` skill)
|
|
28
|
+
- Backend-only or non-UI tasks
|
|
29
|
+
- When you already have the component and only need manual edits
|
|
30
|
+
|
|
31
|
+
## Relationship to Other Skills
|
|
32
|
+
|
|
33
|
+
| Skill | Role |
|
|
34
|
+
|-------|------|
|
|
35
|
+
| `shadcn-ui` | Upstream — ensures shadcn/ui is configured correctly before v0 generation |
|
|
36
|
+
| `frontend-design` | Downstream — use for manual refinement of v0 output, design system application |
|
|
37
|
+
| `design-taste-frontend` | Aesthetic baseline applied to v0 generations |
|
|
38
|
+
|
|
39
|
+
**Pipeline**: `shadcn-ui` → `v0` → `frontend-design`
|
|
40
|
+
|
|
41
|
+
## Prompt Engineering: The 5-Block Component Brief
|
|
42
|
+
|
|
43
|
+
Every v0 prompt should include these 5 blocks. Missing blocks = v0 guesses = expensive iterations.
|
|
44
|
+
|
|
45
|
+
| # | Block | Question | Example |
|
|
46
|
+
|---|-------|----------|---------|
|
|
47
|
+
| 1 | **What it is** | What component? | "A subscription pricing card for a SaaS dashboard" |
|
|
48
|
+
| 2 | **Behavior** | How does it work? | "Three tiers (Free/Pro/Enterprise), monthly/yearly toggle switches pricing, clicking a tier highlights it" |
|
|
49
|
+
| 3 | **States** | What states? | "Loading spinner while pricing loads, error state if fetch fails, empty state if no tiers returned" |
|
|
50
|
+
| 4 | **Props / Data Shape** | What's the contract? | "`tiers: Tier[]`, `billing: 'monthly' | 'yearly'`, `onSelect: (tier) => void`" |
|
|
51
|
+
| 5 | **Visual Intent** | What should it look like? | "shadcn Card layout, data-first, accent color on recommended tier, generous whitespace, no shadows" |
|
|
52
|
+
|
|
53
|
+
### Prompt Patterns
|
|
54
|
+
|
|
55
|
+
| Pattern | Example | Why |
|
|
56
|
+
|---------|---------|-----|
|
|
57
|
+
| **Constraint anchoring** | "Use only shadcn/ui primitives and Tailwind classes" | Prevents unknown imports |
|
|
58
|
+
| **Negative instruction** | "Do not use inline styles or external CSS files" | Eliminates anti-patterns |
|
|
59
|
+
| **Type contract** | "Define TypeScript interface for all props first" | Forces explicit API surface |
|
|
60
|
+
| **Responsive breakpoints** | "Mobile-first: stack on sm, 2-col on md, 3-col on lg" | Correct Tailwind prefixes |
|
|
61
|
+
| **State specification** | "Lift filter state to the parent via onFilterChange" | Proper unidirectional data flow |
|
|
62
|
+
| **System context** | "You are generating for Next.js App Router with shadcn/ui Vega style..." | Foundation for consistency |
|
|
63
|
+
|
|
64
|
+
### Anti-Patterns in Prompts
|
|
65
|
+
|
|
66
|
+
| Anti-Pattern | Fix |
|
|
67
|
+
|-------------|-----|
|
|
68
|
+
| One-word prompt ("Pricing page") | Write a 5-block brief |
|
|
69
|
+
| Generating full pages | Generate component by component, compose pages manually |
|
|
70
|
+
| Hard-coded data | Generate data-less components, wire real data separately |
|
|
71
|
+
| "Make it look like Stripe" | Describe visual intent concretely, not by brand reference |
|
|
72
|
+
| 10+ refinements in one follow-up | 1-2 changes per loop; rewrite brief from scratch if complex |
|
|
73
|
+
| UI-first architecture | Design API/schema first, then generate UI that matches it |
|
|
74
|
+
| Passing API secrets through prompts | Never send secrets through v0 chat |
|
|
75
|
+
|
|
76
|
+
## Integration Paths
|
|
77
|
+
|
|
78
|
+
### Path A — shadcn CLI Pull (Recommended)
|
|
79
|
+
|
|
80
|
+
1. Generate UI on [v0.app](https://v0.app)
|
|
81
|
+
2. Click **"Add to Codebase"** → copy the command:
|
|
82
|
+
```bash
|
|
83
|
+
npx shadcn@latest add "https://v0.dev/chat/b/<project_id>?token=<token>"
|
|
84
|
+
```
|
|
85
|
+
3. Run in your project root — pulls component + installs deps
|
|
86
|
+
|
|
87
|
+
### Path B — v0 CLI
|
|
88
|
+
|
|
89
|
+
```bash
|
|
90
|
+
# One-time setup in existing Next.js project
|
|
91
|
+
npx v0@latest init
|
|
92
|
+
|
|
93
|
+
# Add specific generated component by ID
|
|
94
|
+
npx v0@latest add <id>
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
The `v0` CLI (v2.2.5) installs deps: `@radix-ui/react-icons`, `clsx`, `lucide-react`.
|
|
98
|
+
|
|
99
|
+
### Path C — SDK (Programmatic)
|
|
100
|
+
|
|
101
|
+
```bash
|
|
102
|
+
npm install v0-sdk
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
```typescript
|
|
106
|
+
import { v0 } from 'v0-sdk'
|
|
107
|
+
|
|
108
|
+
const client = v0.createClient({ apiKey: process.env.V0_API_KEY })
|
|
109
|
+
|
|
110
|
+
// Create a chat
|
|
111
|
+
const chat = await client.chats.create({
|
|
112
|
+
message: "Build a pricing card with three tiers and monthly/yearly toggle"
|
|
113
|
+
})
|
|
114
|
+
|
|
115
|
+
// Stream the response
|
|
116
|
+
for await (const chunk of chat.stream()) {
|
|
117
|
+
console.log(chunk)
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// Initialize with existing files
|
|
121
|
+
await client.chats.init({
|
|
122
|
+
files: [{ path: 'components/ui/button.tsx', content: '...' }]
|
|
123
|
+
})
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
### v0 SDK Ecosystem
|
|
127
|
+
|
|
128
|
+
| Package | Version | Purpose |
|
|
129
|
+
|---------|---------|---------|
|
|
130
|
+
| `v0-sdk` | 0.16.4 | Core TypeScript SDK — chats, projects, deployments |
|
|
131
|
+
| `@v0-sdk/react` | 0.5.0 | Headless React components for v0 content |
|
|
132
|
+
| `@v0-sdk/ai-tools` | 0.3.8 | AI SDK tools for agents (~20 tools) |
|
|
133
|
+
| `create-v0-sdk-app` | 0.2.1 | Scaffold SDK-powered apps |
|
|
134
|
+
|
|
135
|
+
### Path D — MCP Server
|
|
136
|
+
|
|
137
|
+
Connect IDEs to v0 via MCP:
|
|
138
|
+
|
|
139
|
+
```json
|
|
140
|
+
{
|
|
141
|
+
"mcpServers": {
|
|
142
|
+
"v0": {
|
|
143
|
+
"command": "npx",
|
|
144
|
+
"args": ["mcp-remote", "https://mcp.v0.dev", "--header", "Authorization: Bearer ${V0_API_KEY}"]
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
Supports: Cursor, Claude Desktop, VS Code, Windsurf, any MCP-compatible IDE.
|
|
151
|
+
|
|
152
|
+
## Platform API (Public Beta)
|
|
153
|
+
|
|
154
|
+
REST API at `https://api.v0.dev/v1`. Covers: chats, projects, deployments, users, webhooks, MCP servers.
|
|
155
|
+
|
|
156
|
+
```typescript
|
|
157
|
+
// Direct REST
|
|
158
|
+
const res = await fetch('https://api.v0.dev/v1/chats', {
|
|
159
|
+
method: 'POST',
|
|
160
|
+
headers: {
|
|
161
|
+
'Authorization': `Bearer ${process.env.V0_API_KEY}`,
|
|
162
|
+
'Content-Type': 'application/json'
|
|
163
|
+
},
|
|
164
|
+
body: JSON.stringify({
|
|
165
|
+
message: "Build a login form with email/password fields"
|
|
166
|
+
})
|
|
167
|
+
})
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
## Integration with shadcn/ui
|
|
171
|
+
|
|
172
|
+
v0 generates code using shadcn/ui by default. Key integration points:
|
|
173
|
+
|
|
174
|
+
- **Before generating**: Run `npx shadcn init` in your project
|
|
175
|
+
- **Custom design systems**: Publish a shadcn/ui Registry with your design tokens → v0 generates brand-consistent output
|
|
176
|
+
- **Registry MCP**: Connect your registry to v0 for design-aware generation
|
|
177
|
+
- **`components.json`**: v0 respects your project's aliases and style config
|
|
178
|
+
- **Check deps**: After pulling v0 code, verify all shadcn components are installed
|
|
179
|
+
|
|
180
|
+
### Design System Integration
|
|
181
|
+
|
|
182
|
+
```json
|
|
183
|
+
// components.json — exposed to v0 for brand-aware generation
|
|
184
|
+
{
|
|
185
|
+
"style": "vega",
|
|
186
|
+
"tsx": true,
|
|
187
|
+
"tailwind": { "cssVariables": true },
|
|
188
|
+
"aliases": { "components": "@/components", "ui": "@/components/ui" }
|
|
189
|
+
}
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
## v0 2.0 Platform (Feb 2026)
|
|
193
|
+
|
|
194
|
+
Key capabilities beyond UI generation:
|
|
195
|
+
|
|
196
|
+
- **Import any GitHub repo** into sandbox with auto-pulled Vercel env vars
|
|
197
|
+
- **Git panel** — branching, PRs, merging from the browser
|
|
198
|
+
- **DB integrations** — Snowflake, AWS databases, Neon + Drizzle scaffolding
|
|
199
|
+
- **Enterprise** — deployment protection, access controls, SSO, signed Git commits
|
|
200
|
+
- **Agents** — plan and build end-to-end agentic workflows
|
|
201
|
+
- **Design Mode** — floating toolbar, layers viewer, measure overlay
|
|
202
|
+
- **Cmd+K** — command palette navigation
|
|
203
|
+
- **Shopify integration** — storefront building
|
|
204
|
+
- **Auto merge conflict resolution**
|
|
205
|
+
|
|
206
|
+
## Best Patterns
|
|
207
|
+
|
|
208
|
+
### Component-First Decomposition
|
|
209
|
+
|
|
210
|
+
Instead of: "Build a dashboard page"
|
|
211
|
+
|
|
212
|
+
Do:
|
|
213
|
+
1. Generate `StatCard` → pull → verify
|
|
214
|
+
2. Generate `DataTable` → pull → verify
|
|
215
|
+
3. Generate `ChartWidget` → pull → verify
|
|
216
|
+
4. Compose in the page manually
|
|
217
|
+
|
|
218
|
+
### System Prompt Template
|
|
219
|
+
|
|
220
|
+
When using the SDK or Platform API, set a system prompt:
|
|
221
|
+
|
|
222
|
+
```
|
|
223
|
+
You are a shadcn/ui component generator.
|
|
224
|
+
Framework: Next.js 15+ App Router.
|
|
225
|
+
Language: TypeScript strict mode.
|
|
226
|
+
Styling: Tailwind CSS v4 with CSS variables.
|
|
227
|
+
Imports: use @/ path alias.
|
|
228
|
+
Data: accept via props, never hard-code.
|
|
229
|
+
States: loading, empty, error, normal.
|
|
230
|
+
Accessibility: ARIA labels, keyboard nav, focus management.
|
|
231
|
+
Dark mode: CSS variable overrides.
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
### v0 → Production Workflow
|
|
235
|
+
|
|
236
|
+
1. **Spec** → Define data model, user flow, design tokens
|
|
237
|
+
2. **Generate** → v0 with 5-block brief for each component
|
|
238
|
+
3. **Pull** → Add to codebase via CLI
|
|
239
|
+
4. **Review** → Check TypeScript types, accessibility, theme compliance
|
|
240
|
+
5. **Wire** → Connect real data sources, auth, API routes
|
|
241
|
+
6. **Harden** → Loading states, error boundaries, edge cases
|
|
242
|
+
7. **Deploy** → Vercel preview + production
|
|
243
|
+
|
|
244
|
+
## Common Pitfalls
|
|
245
|
+
|
|
246
|
+
| Pitfall | Fix |
|
|
247
|
+
|---------|-----|
|
|
248
|
+
| Treating v0 output as production-ready | v0 handles presentation only — you need auth, data, logic |
|
|
249
|
+
| Letting v0 define your data model | Your domain expertise defines the model; v0 adapts |
|
|
250
|
+
| Client-only generation | Plan SSR boundaries; v0 generates client components by default |
|
|
251
|
+
| Not checking shadcn deps before pulling | `npx shadcn info --json` to verify installed components |
|
|
252
|
+
| Generating without design tokens | Set system prompt with CSS variable references |
|
|
253
|
+
| Skipping review | Every v0 component needs human review for correctness |
|
|
254
|
+
|
|
255
|
+
## Verification
|
|
256
|
+
|
|
257
|
+
- [ ] Generated components use project design tokens (CSS variables, not hard-coded hex)
|
|
258
|
+
- [ ] All components accept typed props (no hard-coded data)
|
|
259
|
+
- [ ] Loading, empty, error states handled
|
|
260
|
+
- [ ] Required shadcn/ui primitives installed (`npx shadcn info --json`)
|
|
261
|
+
- [ ] TypeScript compiles without errors
|
|
262
|
+
- [ ] Keyboard navigation and ARIA labels verified
|
|
263
|
+
- [ ] Light and dark mode both render correctly
|
|
264
|
+
- [ ] Components refactored for reusability (not page-specific)
|