@thorprovider/storefront 0.4.0-mvp.0 → 0.4.0-mvp.2

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 (166) hide show
  1. package/README.md +173 -153
  2. package/VENDOR.md +69 -0
  3. package/assets/openspec/config.yaml +38 -0
  4. package/assets/templates/AGENTS.md +76 -0
  5. package/assets/templates/app/app/[[...slug]]/page.tsx +42 -1
  6. package/assets/templates/app/app/[[...slug]]/renderer.tsx +13 -2
  7. package/assets/templates/app/app/globals.css +5 -8
  8. package/assets/templates/app/app/layout.tsx +1 -5
  9. package/assets/templates/app/app/robots.ts +9 -3
  10. package/assets/templates/app/app/sitemap.ts +64 -5
  11. package/assets/templates/app/lib/__STOREFRONT__/actions.ts +70 -0
  12. package/assets/templates/app/lib/__STOREFRONT__/catalog.ts +15 -5
  13. package/assets/templates/app/lib/__STOREFRONT__/commerce.ts +71 -0
  14. package/assets/templates/app/lib/__STOREFRONT__/composition.ts +26 -0
  15. package/assets/templates/app/lib/__STOREFRONT__/handlers.core.ts +36 -4
  16. package/assets/templates/app/lib/__STOREFRONT__/registry.tsx +82 -50
  17. package/assets/templates/app/lib/__STOREFRONT__/runtime.ts +3 -0
  18. package/assets/templates/app/lib/__STOREFRONT__/schema.ts +29 -14
  19. package/assets/templates/app/lib/__STOREFRONT__/section-observation.ts +182 -0
  20. package/assets/templates/app/lib/__STOREFRONT__/section-sources.ts +130 -0
  21. package/assets/templates/app/lib/__STOREFRONT__/shell/content.ts +31 -7
  22. package/assets/templates/app/lib/__STOREFRONT__/shell/props.ts +4 -0
  23. package/assets/templates/app/lib/__STOREFRONT__/spec/index.ts +56 -26
  24. package/assets/templates/app/lib/__STOREFRONT__/spec/seo.ts +165 -0
  25. package/assets/templates/app/lib/__STOREFRONT__/store-context.ts +26 -7
  26. package/assets/templates/app/lib/__STOREFRONT__/views/sections/index.ts +2 -1
  27. package/assets/templates/app/lib/__STOREFRONT__/views/sections/products.client.tsx +100 -0
  28. package/assets/templates/app/lib/__STOREFRONT__/views/sections/products.tsx +19 -0
  29. package/assets/templates/app/lib/__STOREFRONT__/views/sections/schema.products.ts +39 -0
  30. package/assets/templates/features/checkout/handlers.ts +6 -0
  31. package/assets/templates/features/customer-auth/state.ts +10 -1
  32. package/assets/templates/views/account/index.tsx +1 -1
  33. package/assets/templates/views/account/registry.tsx +80 -19
  34. package/assets/templates/views/account/schema.ts +1 -0
  35. package/assets/templates/views/cart/content.ts +11 -0
  36. package/assets/templates/views/cart/data.ts +23 -0
  37. package/assets/templates/views/cart/index.tsx +53 -0
  38. package/assets/templates/views/cart/props.ts +12 -0
  39. package/assets/templates/views/cart/registry.tsx +81 -0
  40. package/assets/templates/views/cart/schema.ts +52 -0
  41. package/assets/templates/views/category/assembly/assembly.json +1 -0
  42. package/assets/templates/views/category/assembly/index.skeleton.tsx +64 -6
  43. package/assets/templates/views/category/assembly/registry.skeleton.tsx +2 -1
  44. package/assets/templates/views/category/assembly/slots/breadcrumb/Breadcrumb/binding.json +5 -0
  45. package/assets/templates/views/category/assembly/slots/breadcrumb/Breadcrumb/catalog.part +4 -0
  46. package/assets/templates/views/category/assembly/slots/breadcrumb/Breadcrumb/element.part +7 -0
  47. package/assets/templates/views/category/assembly/slots/breadcrumb/Breadcrumb/registry.part +18 -0
  48. package/assets/templates/views/category/index.tsx +76 -7
  49. package/assets/templates/views/category/props.ts +7 -0
  50. package/assets/templates/views/category/registry.tsx +19 -0
  51. package/assets/templates/views/category/schema.ts +13 -0
  52. package/assets/templates/views/home/assembly/assembly.json +9 -0
  53. package/assets/templates/views/home/assembly/index.skeleton.tsx +22 -4
  54. package/assets/templates/views/home/assembly/slots/hero/StorefrontHero1/element.part +1 -1
  55. package/assets/templates/views/home/assembly/slots/hero/StorefrontHero1/registry.part +2 -2
  56. package/assets/templates/views/home/data.ts +9 -0
  57. package/assets/templates/views/home/index.tsx +23 -5
  58. package/assets/templates/views/home/registry.tsx +2 -2
  59. package/assets/templates/views/product/data.ts +4 -2
  60. package/assets/templates/views/product/index.tsx +49 -5
  61. package/assets/templates/views/product/props.ts +7 -0
  62. package/assets/templates/views/product/registry.tsx +25 -0
  63. package/assets/templates/views/product/schema.ts +13 -0
  64. package/assets/templates/views/static/content.ts +4 -4
  65. package/assets/templates/views/static/index.tsx +1 -1
  66. package/bin/storefront.js +1231 -154
  67. package/examples/merchandising/category-led.json +37 -0
  68. package/examples/merchandising/collection-led.json +37 -0
  69. package/package.json +11 -7
  70. package/recipes/archetypes.json +9 -22
  71. package/recipes/catalog.json +40 -1
  72. package/recipes/features.json +25 -2
  73. package/recipes/recipe.schema.json +1 -1
  74. package/recipes/recipes.json +32 -30
  75. package/recipes/sections.json +10 -0
  76. package/recipes/validate.mjs +54 -0
  77. package/scripts/candidate.mjs +4 -4
  78. package/scripts/composers.mjs +25 -5
  79. package/scripts/env-file.mjs +86 -0
  80. package/scripts/file-classes.mjs +5 -0
  81. package/scripts/migrate-english-routes.mjs +276 -0
  82. package/scripts/pinning.mjs +74 -5
  83. package/scripts/plan-apply.mjs +149 -5
  84. package/scripts/reconcile.mjs +49 -11
  85. package/scripts/render-templates.mjs +97 -44
  86. package/scripts/section-config.mjs +253 -0
  87. package/scripts/starter.mjs +17 -0
  88. package/scripts/theme/color.mjs +185 -0
  89. package/scripts/theme/css-theme.mjs +233 -0
  90. package/scripts/theme/fonts.mjs +123 -0
  91. package/scripts/theme/import-export.mjs +125 -0
  92. package/scripts/theme/preset.mjs +93 -0
  93. package/scripts/theme/theme-plan.mjs +292 -0
  94. package/scripts/theme/tokens.mjs +137 -0
  95. package/scripts/view-assembly.mjs +47 -1
  96. package/skills/sf-feature/SKILL.md +32 -19
  97. package/skills/sf-setup/SKILL.md +79 -0
  98. package/skills/sf-theme-gen/SKILL.md +99 -36
  99. package/skills/sf-theme-gen/references/shadcn-theming.md +87 -0
  100. package/skills/{shadcn-theming/references/templates → sf-theme-gen/references}/theme-setup.md +15 -19
  101. package/skills/{shadcn-theming → sf-theme-gen}/references/theming-guide.md +6 -11
  102. package/skills/sf-view-gen/SKILL.md +63 -40
  103. package/skills/sf-view-gen/assets/recipes/archetypes.json +9 -22
  104. package/skills/sf-view-gen/assets/recipes/catalog.json +40 -1
  105. package/skills/sf-view-gen/assets/recipes/features.json +25 -2
  106. package/skills/sf-view-gen/assets/recipes/recipes.json +32 -30
  107. package/skills/sf-view-gen/assets/recipes/sections.json +10 -0
  108. package/skills/sf-view-gen/assets/templates/views/sections/products.client.tsx +100 -0
  109. package/skills/sf-view-gen/assets/templates/views/sections/products.tsx +19 -0
  110. package/skills/sf-view-gen/assets/templates/views/sections/schema.products.ts +39 -0
  111. package/skills/{json-render-core/SKILL.md → sf-view-gen/references/json-render-core.md} +4 -27
  112. package/skills/{json-render-next/SKILL.md → sf-view-gen/references/json-render-next.md} +5 -8
  113. package/skills/{json-render-react/SKILL.md → sf-view-gen/references/json-render-react.md} +3 -5
  114. package/skills/{json-render-shadcn/SKILL.md → sf-view-gen/references/json-render-shadcn.md} +3 -5
  115. package/skills/sf-view-gen/references/views.md +28 -37
  116. package/skills/sf-view-gen/scripts/validate.mjs +54 -0
  117. package/skills/storefront-workflow/SKILL.md +94 -31
  118. package/skills/{storefront-best-practices/reference/design.md → storefront-workflow/references/design-consistency.md} +13 -29
  119. package/CHANGELOG.md +0 -84
  120. package/assets/templates/views/user-panel/index.tsx +0 -106
  121. package/assets/templates/views/user-panel/registry.tsx +0 -54
  122. package/assets/templates/views/user-panel/schema.ts +0 -79
  123. package/commands/sf-add-view.md +0 -57
  124. package/commands/sf-feature.md +0 -28
  125. package/commands/sf-theme.md +0 -26
  126. package/commands/sf-view.md +0 -67
  127. package/skills/building-storefronts/SKILL.md +0 -178
  128. package/skills/building-storefronts/references/frontend-integration.md +0 -229
  129. package/skills/json-render-remotion/SKILL.md +0 -111
  130. package/skills/json-render-solid/SKILL.md +0 -204
  131. package/skills/nextjs-shadcn/SKILL.md +0 -367
  132. package/skills/nextjs-shadcn/references/architecture.md +0 -499
  133. package/skills/nextjs-shadcn/references/project-setup.md +0 -162
  134. package/skills/nextjs-shadcn/references/shadcn-platform.md +0 -261
  135. package/skills/nextjs-shadcn/references/sidebar.md +0 -274
  136. package/skills/nextjs-shadcn/references/styling.md +0 -555
  137. package/skills/shadcn-component-discovery/SKILL.md +0 -273
  138. package/skills/shadcn-component-discovery/references/registries.md +0 -226
  139. package/skills/shadcn-theming/SKILL.md +0 -104
  140. package/skills/storefront-best-practices/SKILL.md +0 -421
  141. package/skills/storefront-best-practices/reference/components/breadcrumbs.md +0 -123
  142. package/skills/storefront-best-practices/reference/components/cart-popup.md +0 -189
  143. package/skills/storefront-best-practices/reference/components/country-selector.md +0 -298
  144. package/skills/storefront-best-practices/reference/components/footer.md +0 -112
  145. package/skills/storefront-best-practices/reference/components/hero.md +0 -241
  146. package/skills/storefront-best-practices/reference/components/megamenu.md +0 -239
  147. package/skills/storefront-best-practices/reference/components/navbar.md +0 -397
  148. package/skills/storefront-best-practices/reference/components/popups.md +0 -221
  149. package/skills/storefront-best-practices/reference/components/product-card.md +0 -125
  150. package/skills/storefront-best-practices/reference/components/product-reviews.md +0 -217
  151. package/skills/storefront-best-practices/reference/components/product-slider.md +0 -174
  152. package/skills/storefront-best-practices/reference/components/search.md +0 -101
  153. package/skills/storefront-best-practices/reference/connecting-to-backend.md +0 -391
  154. package/skills/storefront-best-practices/reference/features/promotions.md +0 -307
  155. package/skills/storefront-best-practices/reference/features/wishlist.md +0 -230
  156. package/skills/storefront-best-practices/reference/layouts/account.md +0 -380
  157. package/skills/storefront-best-practices/reference/layouts/cart.md +0 -316
  158. package/skills/storefront-best-practices/reference/layouts/checkout.md +0 -486
  159. package/skills/storefront-best-practices/reference/layouts/home-page.md +0 -264
  160. package/skills/storefront-best-practices/reference/layouts/order-confirmation.md +0 -231
  161. package/skills/storefront-best-practices/reference/layouts/product-details.md +0 -527
  162. package/skills/storefront-best-practices/reference/layouts/product-listing.md +0 -520
  163. package/skills/storefront-best-practices/reference/layouts/static-pages.md +0 -356
  164. package/skills/storefront-best-practices/reference/medusa.md +0 -307
  165. /package/skills/{storefront-best-practices/reference → storefront-workflow/references}/mobile-responsiveness.md +0 -0
  166. /package/skills/{storefront-best-practices/reference → storefront-workflow/references}/seo.md +0 -0
@@ -1,499 +0,0 @@
1
- # Architecture
2
-
3
- ## Best Practices
4
-
5
- - Avoid `useEffect` - prefer Server Components, Server Actions, or event handlers
6
- - `"use client"` only at leaf components (smallest boundary)
7
- - Props must be serializable (data or Server Actions, no functions/classes)
8
- - Prefer Tailwind v4 `globals.css` theme variables over hardcoded values
9
-
10
- ## Component Patterns
11
-
12
- ### Server vs Client Decision Tree
13
-
14
- ```
15
- Need state/effects/browser APIs?
16
- ├── Yes → "use client" at smallest boundary
17
- └── No → Server Component (default)
18
-
19
- Passing data to client?
20
- ├── Functions/classes → ❌ Not serializable
21
- ├── Plain objects/arrays → ✅ Props
22
- └── Server logic → ✅ Server Actions
23
- ```
24
-
25
- ### Component Placement
26
-
27
- ```
28
- app/
29
- ├── (protected)/ # Auth required routes
30
- │ ├── dashboard/
31
- │ ├── settings/
32
- │ ├── components/ # Route-specific components
33
- │ └── lib/ # Route-specific types/utils
34
- ├── (public)/ # Public routes
35
- │ ├── login/
36
- │ └── register/
37
- ├── actions/ # Server Actions (global)
38
- ├── api/ # API routes
39
- components/ # Shared across routes
40
- ├── ui/ # shadcn primitives
41
- └── shared/ # Business components
42
- hooks/ # Custom React hooks
43
- lib/ # Shared utilities
44
- data/ # Database queries
45
- ai/ # AI logic (tools, agents, prompts)
46
- ```
47
-
48
- ### AI Directory Structure
49
-
50
- When building AI applications, organize the `ai/` directory:
51
-
52
- ```
53
- ai/
54
- ├── model-names.ts # Model definitions & DEFAULT_MODEL_NAME
55
- ├── actions/ # AI-related server actions
56
- │ ├── model.ts # saveModelId, getModelId (cookie-based)
57
- │ └── chat.ts # Chat-related actions
58
- ├── utils.ts # findSources, getLastUserMessageText, etc.
59
- ├── agents/ # Agent definitions (if using agents)
60
- │ └── assistant.ts
61
- └── tools/ # Tool definitions (if using tools)
62
- ```
63
-
64
- **model-names.ts example:**
65
-
66
- ```ts
67
- export interface Model {
68
- id: string
69
- label: string
70
- description: string
71
- }
72
-
73
- export const models: Model[] = [
74
- { id: "gpt-5.6-luna", label: "GPT 5.6 Luna", description: "Fast, lightweight tasks" },
75
- { id: "gpt-5.6", label: "GPT 5.6", description: "Complex, multi-step tasks" },
76
- ]
77
-
78
- export const DEFAULT_MODEL_NAME = "gpt-5.6-luna"
79
- ```
80
-
81
- **Cookie-based model storage:**
82
-
83
- ```ts
84
- // ai/actions/model.ts - Server Action for MUTATION only
85
- "use server"
86
- import { cookies } from "next/headers"
87
-
88
- export async function saveModelId(model: string) {
89
- const cookieStore = await cookies()
90
- cookieStore.set("model-id", model)
91
- }
92
-
93
- // ❌ WRONG: Don't use Server Action for reading data
94
- // export async function getModelId() { ... }
95
-
96
- // ✅ CORRECT: Read cookies directly in Server Component
97
- // page.tsx
98
- import { cookies } from "next/headers"
99
-
100
- export default async function Page() {
101
- const cookieStore = await cookies()
102
- const modelId = cookieStore.get("model-id")?.value ?? DEFAULT_MODEL_NAME
103
- return <Chat modelId={modelId} />
104
- }
105
- ```
106
-
107
- ### className Pattern
108
-
109
- Always accept and merge `className`:
110
-
111
- ```tsx
112
- import { cn } from "@/lib/utils"
113
-
114
- interface CardProps extends React.HTMLAttributes<HTMLDivElement> {
115
- variant?: "default" | "outline"
116
- }
117
-
118
- export function Card({ className, variant = "default", ...props }: CardProps) {
119
- return (
120
- <div
121
- className={cn(
122
- "rounded-lg p-4",
123
- variant === "outline" && "border",
124
- className
125
- )}
126
- {...props}
127
- />
128
- )
129
- }
130
- ```
131
-
132
- ## Data Fetching Patterns
133
-
134
- ### Server Component (default)
135
-
136
- Fetch data directly in Server Components:
137
-
138
- ```tsx
139
- export default async function Page() {
140
- const data = await fetchData()
141
- return <Component data={data} />
142
- }
143
- ```
144
-
145
- ### Cached Data Function
146
-
147
- Use `'use cache'` for reusable cached queries. `"use cache"` (and `cacheTag`/`cacheLife`/`updateTag`) requires the Cache Components opt-in flag — Next.js 16 does not enable it by default:
148
-
149
- ```ts
150
- // next.config.ts
151
- const nextConfig = { cacheComponents: true }
152
- ```
153
-
154
- ```tsx
155
- // data/products.ts
156
- export async function getProducts() {
157
- "use cache"
158
- cacheTag("products")
159
- cacheLife("hours")
160
- return await db.products.findMany()
161
- }
162
- ```
163
-
164
- ### Streaming to Client (React `use` hook)
165
-
166
- Pass promises to Client Components for streaming:
167
-
168
- ```tsx
169
- // Server Component
170
- export default function Page() {
171
- const dataPromise = fetchData() // Don't await
172
- return (
173
- <Suspense fallback={<Loading />}>
174
- <ClientDisplay dataPromise={dataPromise} />
175
- </Suspense>
176
- )
177
- }
178
-
179
- // Client Component
180
- "use client"
181
- import { use } from "react"
182
-
183
- export function ClientDisplay({ dataPromise }: { dataPromise: Promise<Data> }) {
184
- const data = use(dataPromise) // Suspends until resolved
185
- return <Chart data={data} />
186
- }
187
- ```
188
-
189
- ### Explicit Request-time with connection()
190
-
191
- Use `connection()` to explicitly defer to request time without accessing runtime APIs:
192
-
193
- ```tsx
194
- import { connection } from "next/server"
195
- import { Suspense } from "react"
196
-
197
- async function UniqueContent() {
198
- await connection() // Defer to request time
199
- const uuid = crypto.randomUUID()
200
- const timestamp = Date.now()
201
- return <div>{uuid} - {timestamp}</div>
202
- }
203
-
204
- export default function Page() {
205
- return (
206
- <Suspense fallback={<Loading />}>
207
- <UniqueContent />
208
- </Suspense>
209
- )
210
- }
211
- ```
212
-
213
- **When to use `connection()`:**
214
-
215
- | Scenario | Use connection()? |
216
- |----------|-------------------|
217
- | Need unique values per request | ✅ Yes |
218
- | Using `Math.random()`, `Date.now()`, `crypto.randomUUID()` | ✅ Yes |
219
- | Already using `cookies()` or `headers()` | ❌ No (not needed) |
220
- | Data is cacheable | ❌ No (use `'use cache'`) |
221
-
222
- ## Routing
223
-
224
- ### Route Groups
225
-
226
- Group routes without affecting URL:
227
-
228
- ```
229
- app/
230
- ├── (protected)/ # Auth required - /dashboard, /settings
231
- │ ├── dashboard/
232
- │ ├── settings/
233
- │ └── layout.tsx # Shared chrome (sidebar, auth check)
234
- ├── (public)/ # Public - /login, /register, /about
235
- │ ├── login/
236
- │ ├── register/
237
- │ └── about/
238
- └── (marketing)/ # Marketing - /pricing, /features
239
- ├── pricing/
240
- └── features/
241
- ```
242
-
243
- ### Layout vs Template
244
-
245
- | Aspect | layout.tsx | template.tsx |
246
- |--------|------------|--------------|
247
- | State | Persists across navigation | Resets on navigation |
248
- | Effects | Run once | Run on every navigation |
249
- | Use when | Shared chrome (nav, footer) | Analytics, animations that reset |
250
-
251
- **Decision tree:**
252
- ```
253
- State/effects should reset on navigation?
254
- ├── Yes → template.tsx
255
- └── No → layout.tsx (default)
256
- ```
257
-
258
- ### Async Params (Next.js 16)
259
-
260
- ```tsx
261
- // Always await params and searchParams
262
- export default async function Page({
263
- params,
264
- searchParams,
265
- }: {
266
- params: Promise<{ slug: string }>
267
- searchParams: Promise<{ page?: string }>
268
- }) {
269
- const { slug } = await params
270
- const { page = "1" } = await searchParams
271
-
272
- const data = await fetchData(slug, parseInt(page))
273
- return <Content data={data} />
274
- }
275
- ```
276
-
277
- ## Suspense Strategy
278
-
279
- ### When to Use Suspense
280
-
281
- ```
282
- Slow data fetch in Server Component?
283
- ├── Yes → Wrap in <Suspense>
284
- └── No → Direct render
285
-
286
- Multiple independent slow sections?
287
- ├── Yes → Separate <Suspense> boundaries
288
- └── No → Single boundary or loading.tsx
289
- ```
290
-
291
- ### Patterns
292
-
293
- **loading.tsx** - Entire route fallback:
294
- ```tsx
295
- // app/dashboard/loading.tsx
296
- export default function Loading() {
297
- return <DashboardSkeleton />
298
- }
299
- ```
300
-
301
- **Suspense** - Granular streaming:
302
- ```tsx
303
- export default function Page() {
304
- return (
305
- <>
306
- <Header /> {/* Renders immediately */}
307
- <Suspense fallback={<StatsSkeleton />}>
308
- <SlowStats /> {/* Streams when ready */}
309
- </Suspense>
310
- <Suspense fallback={<ChartSkeleton />}>
311
- <SlowChart /> {/* Streams independently */}
312
- </Suspense>
313
- </>
314
- )
315
- }
316
- ```
317
-
318
- **Skeleton pattern** - Create a skeleton component for each loadable content:
319
- ```tsx
320
- // components/skeletons.tsx
321
- export function CardSkeleton() {
322
- return (
323
- <div className="animate-pulse">
324
- <div className="h-4 bg-muted rounded w-3/4 mb-2" />
325
- <div className="h-4 bg-muted rounded w-1/2" />
326
- </div>
327
- )
328
- }
329
-
330
- export function TableSkeleton({ rows = 5 }: { rows?: number }) {
331
- return (
332
- <div className="space-y-2">
333
- {Array.from({ length: rows }).map((_, i) => (
334
- <div key={i} className="h-10 bg-muted rounded animate-pulse" />
335
- ))}
336
- </div>
337
- )
338
- }
339
- ```
340
-
341
- **Passing promises to client**:
342
- ```tsx
343
- // Server Component
344
- export default function Page() {
345
- const dataPromise = fetchData() // Start fetch, don't await
346
- return <ClientChart dataPromise={dataPromise} />
347
- }
348
-
349
- // Client Component
350
- "use client"
351
- import { use } from "react"
352
-
353
- export function ClientChart({ dataPromise }) {
354
- const data = use(dataPromise) // Suspends until resolved
355
- return <Chart data={data} />
356
- }
357
- ```
358
-
359
- ## State Management
360
-
361
- ### useTransition Pattern
362
-
363
- Wrap non-urgent UI updates to keep interactions smooth:
364
-
365
- ```tsx
366
- "use client"
367
- import { useTransition } from "react"
368
-
369
- function SubmitButton({ action }: { action: () => Promise<void> }) {
370
- const [isPending, startTransition] = useTransition()
371
-
372
- return (
373
- <button
374
- onClick={() => startTransition(() => action())}
375
- disabled={isPending}
376
- >
377
- {isPending ? "Saving..." : "Save"}
378
- </button>
379
- )
380
- }
381
- ```
382
-
383
- **Guidelines:**
384
- - Use `isPending` for feedback (disable buttons, show spinners)
385
- - Don't wrap controlled input state in transitions
386
- - After `await` inside transition, wrap subsequent `setState` in another `startTransition`
387
-
388
- ## Data Patterns
389
-
390
- ### "use cache" (Next.js 16)
391
-
392
- Function-level caching:
393
-
394
- ```tsx
395
- "use cache"
396
-
397
- export async function getProducts() {
398
- const products = await db.query.products.findMany()
399
- return products
400
- }
401
-
402
- // With cache tags
403
- import { cacheTag } from "next/cache"
404
-
405
- export async function getProduct(id: string) {
406
- "use cache"
407
- cacheTag(`product-${id}`)
408
- return db.query.products.findFirst({ where: eq(products.id, id) })
409
- }
410
- ```
411
-
412
- ### Server Actions
413
-
414
- ```tsx
415
- "use server"
416
-
417
- import { updateTag, revalidateTag } from "next/cache"
418
- import { z } from "zod"
419
-
420
- const schema = z.object({
421
- title: z.string().min(1),
422
- content: z.string(),
423
- })
424
-
425
- export async function createPost(formData: FormData) {
426
- const parsed = schema.parse({
427
- title: formData.get("title"),
428
- content: formData.get("content"),
429
- })
430
-
431
- await db.insert(posts).values(parsed)
432
-
433
- // Read-your-writes (immediate)
434
- updateTag("posts")
435
-
436
- // Or SWR-style revalidation
437
- // revalidateTag("posts", "max")
438
- }
439
-
440
- // Refresh uncached data
441
- import { refresh } from "next/cache"
442
-
443
- export async function updateProfile(data: FormData) {
444
- await db.update(...)
445
- refresh() // Triggers client router refresh
446
- }
447
- ```
448
-
449
- ### Proxy API (Next.js 16)
450
-
451
- Replaces middleware for request interception. Place at project root (same level as `app/`):
452
-
453
- ```tsx
454
- // proxy.ts (project root)
455
- import { NextResponse } from "next/server"
456
- import type { NextRequest } from "next/server"
457
-
458
- export async function proxy(request: NextRequest) {
459
- const session = request.cookies.get("session")
460
-
461
- if (!session && request.nextUrl.pathname.startsWith("/dashboard")) {
462
- return NextResponse.redirect(new URL("/login", request.url))
463
- }
464
-
465
- return NextResponse.next()
466
- }
467
-
468
- export const config = {
469
- matcher: ['/dashboard/:path*', '/api/:path*'],
470
- }
471
- ```
472
-
473
- ### Request APIs
474
-
475
- All request APIs are async in Next.js 16:
476
-
477
- ```tsx
478
- import { cookies, headers, draftMode } from "next/headers"
479
-
480
- export default async function Page() {
481
- const cookieStore = await cookies()
482
- const headersList = await headers()
483
- const { isEnabled } = await draftMode()
484
- }
485
- ```
486
-
487
- ## Error Handling
488
-
489
- Define only when custom UX needed:
490
-
491
- ```
492
- app/
493
- ├── error.tsx # Route-level errors
494
- ├── global-error.tsx # Root layout errors
495
- ├── not-found.tsx # 404 pages
496
- └── loading.tsx # Loading states
497
- ```
498
-
499
- Otherwise inherit from parent segment.
@@ -1,162 +0,0 @@
1
- # Project Setup
2
-
3
- ## Create New Project
4
-
5
- ### Minimal Setup
6
-
7
- Use the CLI to scaffold a Next.js project directly. Supply every prompt value as
8
- a flag so the command never reads stdin:
9
-
10
- ```bash
11
- yarn dlx shadcn@latest init \
12
- --template next --base base --preset <preset> \
13
- --no-monorepo --no-rtl --yes --name <project-name>
14
- ```
15
-
16
- `--base` picks the primitive library: `base` (Base UI, default), `radix`, or
17
- `aria` (React Aria). Component APIs differ per base — see
18
- [shadcn-platform.md](shadcn-platform.md). Add `--rtl` if the app needs
19
- right-to-left support.
20
-
21
- `--name <project-name>` makes the CLI create a **new nested project directory**
22
- (`<cwd>/<project-name>/`). Omit it to initialize **in place** in the current
23
- directory (or in `--cwd`). In-place init requires an existing `package.json` —
24
- with an empty directory the wizard always creates the nested project, and the
25
- escape `--name .` fails with `dest already exists`. For an empty directory, run
26
- `yarn create next-app . --ts --tailwind --app --import-alias "@/*"` first
27
- (moving any `.opencode/` aside and restoring it
28
- after — `create-next-app` refuses non-empty dirs), then `shadcn init` in place.
29
- The Thor scaffold flow (`npx @thorprovider/storefront create`) follows exactly that
30
- two-step flow and never uses shadcn's `--name`, so the project has a single root. Any
31
- interactive prompt means a flag is missing — resolve it from `--help`, never by guessing.
32
-
33
- ### Yarn linker (required)
34
-
35
- Write `.yarnrc.yml` at the project root **before installing dependencies**:
36
-
37
- ```yaml
38
- nodeLinker: node-modules
39
- ```
40
-
41
- Yarn 4 defaults to the Plug'n'Play (PnP) linker, which Next.js 16 Turbopack cannot
42
- resolve from (`Could not find the Next.js package`). The `node-modules` linker makes
43
- Yarn create a real `node_modules/`, which Turbopack reads. Vercel does not plan PnP
44
- support for Turbopack.
45
-
46
- ### With Preset Code
47
-
48
- ```bash
49
- yarn dlx shadcn@latest init \
50
- --template next --base base --preset <CODE> \
51
- --no-monorepo --no-rtl --yes --name <project-name>
52
- ```
53
-
54
- Preset codes are short opaque strings from [ui.shadcn.com/create](https://ui.shadcn.com/create) — pass them through verbatim rather than expanding them into individual flags. Prefer this when you want a specific visual system without hardcoding individual style, font, or icon choices into the skill:
55
-
56
- ```bash
57
- yarn dlx shadcn@latest init \
58
- --template next --base base --preset b4h07r5A1 \
59
- --no-monorepo --no-rtl --yes --name my-app
60
- ```
61
-
62
- `shadcn preset decode <code>` prints what a code contains (style, base color,
63
- theme, font, icon library, radius) if you need to read it before applying.
64
-
65
- For a project that already exists, apply a preset instead of re-running `init`:
66
-
67
- ```bash
68
- yarn dlx shadcn@latest apply <CODE> # theme, fonts, components
69
- yarn dlx shadcn@latest apply <CODE> --only theme # just the theme
70
- ```
71
-
72
- ### Monorepo
73
-
74
- ```bash
75
- yarn dlx shadcn@latest init \
76
- --template next \
77
- --monorepo
78
- ```
79
-
80
- ### Existing Project
81
-
82
- If the Next.js app already exists, run the initializer from the project root:
83
-
84
- ```bash
85
- yarn dlx shadcn@latest init \
86
- --template next --base base --preset <preset> \
87
- --no-monorepo --no-rtl --yes
88
- ```
89
-
90
- ### Inspect Before Changing
91
-
92
- Use the CLI to inspect project state or preview registry changes before writing files:
93
-
94
- ```bash
95
- yarn dlx shadcn@latest info
96
- yarn dlx shadcn@latest add button --dry-run
97
- yarn dlx shadcn@latest docs button
98
- ```
99
-
100
- ## Add Components
101
-
102
- ```bash
103
- # Single component
104
- yarn dlx shadcn@latest add button
105
-
106
- # Multiple components
107
- yarn dlx shadcn@latest add button card input
108
-
109
- # All components
110
- yarn dlx shadcn@latest add --all
111
- ```
112
-
113
- ## Common Dependencies
114
-
115
- ```bash
116
- # Forms
117
- yarn add react-hook-form @hookform/resolvers zod
118
-
119
- # AI
120
- yarn add ai @ai-sdk/anthropic
121
-
122
- # Animation
123
- yarn add motion # For Motion
124
- yarn add gsap @gsap/react # For GSAP
125
-
126
- # Icons (pick one)
127
- yarn add lucide-react # Default
128
- ```
129
-
130
- ## Project Structure After Setup
131
-
132
- ```
133
- project/
134
- ├── app/
135
- │ ├── globals.css # Theme tokens
136
- │ ├── layout.tsx # Root layout
137
- │ └── page.tsx # Home page
138
- ├── components/
139
- │ └── ui/ # shadcn components
140
- ├── lib/
141
- │ └── utils.ts # cn() helper
142
- ├── public/
143
- ├── components.json # shadcn config
144
- ├── tsconfig.json
145
- └── package.json
146
- ```
147
-
148
- ## Yarn Commands Reference
149
-
150
- | Task | Command |
151
- |------|---------|
152
- | Install deps | `yarn install` |
153
- | Add package | `yarn add package` |
154
- | Dev server | `yarn dev` |
155
- | Build | `yarn build` |
156
- | Start prod | `yarn start` |
157
- | Add shadcn component | `yarn dlx shadcn@latest add component` |
158
- | Create project | `yarn dlx shadcn@latest init --template next --base base --preset <preset> --no-monorepo --no-rtl --yes --name <project-name>` |
159
- | Read project config | `yarn dlx shadcn@latest info --json` |
160
- | Component API docs | `yarn dlx shadcn@latest docs component` |
161
-
162
-