red64-cli 0.1.0 → 0.2.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 (103) hide show
  1. package/dist/cli/parseArgs.d.ts.map +1 -1
  2. package/dist/cli/parseArgs.js +5 -0
  3. package/dist/cli/parseArgs.js.map +1 -1
  4. package/dist/components/init/CompleteStep.d.ts.map +1 -1
  5. package/dist/components/init/CompleteStep.js +2 -2
  6. package/dist/components/init/CompleteStep.js.map +1 -1
  7. package/dist/components/init/TestCheckStep.d.ts +16 -0
  8. package/dist/components/init/TestCheckStep.d.ts.map +1 -0
  9. package/dist/components/init/TestCheckStep.js +120 -0
  10. package/dist/components/init/TestCheckStep.js.map +1 -0
  11. package/dist/components/init/index.d.ts +1 -0
  12. package/dist/components/init/index.d.ts.map +1 -1
  13. package/dist/components/init/index.js +1 -0
  14. package/dist/components/init/index.js.map +1 -1
  15. package/dist/components/init/types.d.ts +9 -0
  16. package/dist/components/init/types.d.ts.map +1 -1
  17. package/dist/components/screens/InitScreen.d.ts.map +1 -1
  18. package/dist/components/screens/InitScreen.js +69 -6
  19. package/dist/components/screens/InitScreen.js.map +1 -1
  20. package/dist/components/screens/StartScreen.d.ts.map +1 -1
  21. package/dist/components/screens/StartScreen.js +89 -3
  22. package/dist/components/screens/StartScreen.js.map +1 -1
  23. package/dist/services/ConfigService.d.ts +1 -0
  24. package/dist/services/ConfigService.d.ts.map +1 -1
  25. package/dist/services/ConfigService.js.map +1 -1
  26. package/dist/services/ProjectDetector.d.ts +28 -0
  27. package/dist/services/ProjectDetector.d.ts.map +1 -0
  28. package/dist/services/ProjectDetector.js +236 -0
  29. package/dist/services/ProjectDetector.js.map +1 -0
  30. package/dist/services/TestRunner.d.ts +46 -0
  31. package/dist/services/TestRunner.d.ts.map +1 -0
  32. package/dist/services/TestRunner.js +85 -0
  33. package/dist/services/TestRunner.js.map +1 -0
  34. package/dist/services/index.d.ts +2 -0
  35. package/dist/services/index.d.ts.map +1 -1
  36. package/dist/services/index.js +2 -0
  37. package/dist/services/index.js.map +1 -1
  38. package/dist/types/index.d.ts +1 -0
  39. package/dist/types/index.d.ts.map +1 -1
  40. package/dist/types/index.js.map +1 -1
  41. package/framework/agents/claude/.claude/agents/red64/spec-impl.md +131 -2
  42. package/framework/agents/claude/.claude/commands/red64/spec-impl.md +24 -0
  43. package/framework/agents/codex/.codex/agents/red64/spec-impl.md +131 -2
  44. package/framework/agents/codex/.codex/commands/red64/spec-impl.md +24 -0
  45. package/framework/stacks/generic/feedback.md +80 -0
  46. package/framework/stacks/nextjs/accessibility.md +437 -0
  47. package/framework/stacks/nextjs/api.md +431 -0
  48. package/framework/stacks/nextjs/coding-style.md +282 -0
  49. package/framework/stacks/nextjs/commenting.md +226 -0
  50. package/framework/stacks/nextjs/components.md +411 -0
  51. package/framework/stacks/nextjs/conventions.md +333 -0
  52. package/framework/stacks/nextjs/css.md +310 -0
  53. package/framework/stacks/nextjs/error-handling.md +442 -0
  54. package/framework/stacks/nextjs/feedback.md +124 -0
  55. package/framework/stacks/nextjs/migrations.md +332 -0
  56. package/framework/stacks/nextjs/models.md +362 -0
  57. package/framework/stacks/nextjs/queries.md +410 -0
  58. package/framework/stacks/nextjs/responsive.md +338 -0
  59. package/framework/stacks/nextjs/tech-stack.md +177 -0
  60. package/framework/stacks/nextjs/test-writing.md +475 -0
  61. package/framework/stacks/nextjs/validation.md +467 -0
  62. package/framework/stacks/python/api.md +468 -0
  63. package/framework/stacks/python/authentication.md +342 -0
  64. package/framework/stacks/python/code-quality.md +283 -0
  65. package/framework/stacks/python/code-refactoring.md +315 -0
  66. package/framework/stacks/python/coding-style.md +462 -0
  67. package/framework/stacks/python/conventions.md +399 -0
  68. package/framework/stacks/python/error-handling.md +512 -0
  69. package/framework/stacks/python/feedback.md +92 -0
  70. package/framework/stacks/python/implement-ai-llm.md +468 -0
  71. package/framework/stacks/python/migrations.md +388 -0
  72. package/framework/stacks/python/models.md +399 -0
  73. package/framework/stacks/python/python.md +232 -0
  74. package/framework/stacks/python/queries.md +451 -0
  75. package/framework/stacks/python/structure.md +245 -58
  76. package/framework/stacks/python/tech.md +92 -35
  77. package/framework/stacks/python/testing.md +380 -0
  78. package/framework/stacks/python/validation.md +471 -0
  79. package/framework/stacks/rails/authentication.md +176 -0
  80. package/framework/stacks/rails/code-quality.md +287 -0
  81. package/framework/stacks/rails/code-refactoring.md +299 -0
  82. package/framework/stacks/rails/feedback.md +130 -0
  83. package/framework/stacks/rails/implement-ai-llm-with-rubyllm.md +342 -0
  84. package/framework/stacks/rails/rails.md +301 -0
  85. package/framework/stacks/rails/rails8-best-practices.md +498 -0
  86. package/framework/stacks/rails/rails8-css.md +573 -0
  87. package/framework/stacks/rails/structure.md +140 -0
  88. package/framework/stacks/rails/tech.md +108 -0
  89. package/framework/stacks/react/code-quality.md +521 -0
  90. package/framework/stacks/react/components.md +625 -0
  91. package/framework/stacks/react/data-fetching.md +586 -0
  92. package/framework/stacks/react/feedback.md +110 -0
  93. package/framework/stacks/react/forms.md +694 -0
  94. package/framework/stacks/react/performance.md +640 -0
  95. package/framework/stacks/react/product.md +22 -9
  96. package/framework/stacks/react/state-management.md +472 -0
  97. package/framework/stacks/react/structure.md +351 -44
  98. package/framework/stacks/react/tech.md +219 -30
  99. package/framework/stacks/react/testing.md +690 -0
  100. package/package.json +1 -1
  101. package/framework/stacks/node/product.md +0 -27
  102. package/framework/stacks/node/structure.md +0 -82
  103. package/framework/stacks/node/tech.md +0 -63
@@ -0,0 +1,226 @@
1
+ # Code Commenting Standards
2
+
3
+ Documentation and commenting conventions for TypeScript and React codebases, favoring self-documenting code with strategic comments.
4
+
5
+ ---
6
+
7
+ ## Philosophy
8
+
9
+ - **Code is the source of truth**: Comments explain why, not what
10
+ - **Self-documenting first**: Clear names and structure beat comments every time
11
+ - **Evergreen only**: No temporal comments about recent changes or fixes
12
+ - **Public APIs deserve docs**: JSDoc for exported functions, types, and components
13
+
14
+ ---
15
+
16
+ ## When to Comment vs Self-Document
17
+
18
+ ### Do Not Comment
19
+
20
+ ```typescript
21
+ // BAD: Comment restates the code
22
+ // Check if user is active
23
+ if (user.isActive) { ... }
24
+
25
+ // BAD: Comment about a recent change
26
+ // Fixed bug where users could submit empty names (PR #142)
27
+ const name = formData.get("name");
28
+
29
+ // BAD: Commented-out code
30
+ // const oldLogic = computeLegacyScore(user);
31
+ const score = computeScore(user);
32
+ ```
33
+
34
+ ### Do Comment
35
+
36
+ ```typescript
37
+ // GOOD: Explains a non-obvious business rule
38
+ // Users created before 2023 have unlimited storage due to legacy pricing
39
+ if (user.createdAt < LEGACY_CUTOFF_DATE) {
40
+ return Infinity;
41
+ }
42
+
43
+ // GOOD: Explains a workaround
44
+ // Prisma does not support upsert with composite keys in SQLite,
45
+ // so we use a transaction with findFirst + create/update instead
46
+ await prisma.$transaction(async (tx) => { ... });
47
+
48
+ // GOOD: Documents a performance decision
49
+ // Batched in chunks of 100 to avoid exceeding Postgres parameter limit (65535)
50
+ for (const chunk of chunks(items, 100)) { ... }
51
+ ```
52
+
53
+ ---
54
+
55
+ ## JSDoc for Public APIs
56
+
57
+ ### Exported Functions
58
+
59
+ ```typescript
60
+ /**
61
+ * Formats a date relative to now (e.g., "2 hours ago", "yesterday").
62
+ *
63
+ * Falls back to absolute date format for dates older than 7 days.
64
+ *
65
+ * @param date - The date to format
66
+ * @param locale - BCP 47 locale string (defaults to "en-US")
67
+ * @returns Formatted relative or absolute date string
68
+ *
69
+ * @example
70
+ * ```ts
71
+ * formatRelativeDate(new Date()) // "just now"
72
+ * formatRelativeDate(subDays(new Date(), 2)) // "2 days ago"
73
+ * ```
74
+ */
75
+ export function formatRelativeDate(date: Date, locale = "en-US"): string {
76
+ // ...
77
+ }
78
+ ```
79
+
80
+ ### Exported Types
81
+
82
+ ```typescript
83
+ /**
84
+ * Result of a server action that may fail with field-level errors.
85
+ *
86
+ * @typeParam T - The shape of field errors (keys are field names, values are error messages)
87
+ */
88
+ export type ActionResult<T extends Record<string, string[]> = Record<string, string[]>> =
89
+ | { success: true }
90
+ | { success: false; error: string; fieldErrors?: T };
91
+ ```
92
+
93
+ ### React Components
94
+
95
+ ```typescript
96
+ /**
97
+ * Displays a user avatar with fallback initials.
98
+ *
99
+ * Renders the user's profile image when available, otherwise shows
100
+ * initials derived from the user's name on a colored background.
101
+ *
102
+ * @example
103
+ * ```tsx
104
+ * <UserAvatar user={currentUser} size="lg" />
105
+ * <UserAvatar user={currentUser} size="sm" className="ring-2 ring-white" />
106
+ * ```
107
+ */
108
+ export function UserAvatar({ user, size = "md", className }: UserAvatarProps) {
109
+ // ...
110
+ }
111
+ ```
112
+
113
+ ---
114
+
115
+ ## TSDoc Tags Reference
116
+
117
+ | Tag | Usage |
118
+ |---|---|
119
+ | `@param name` | Describe a function parameter |
120
+ | `@returns` | Describe the return value |
121
+ | `@throws` | Document exceptions that may be thrown |
122
+ | `@example` | Provide usage examples (fenced code blocks) |
123
+ | `@typeParam T` | Describe a generic type parameter |
124
+ | `@see` | Reference related functions or documentation |
125
+ | `@deprecated` | Mark as deprecated with migration guidance |
126
+ | `@internal` | Mark as not part of the public API |
127
+
128
+ ### Deprecation Pattern
129
+
130
+ ```typescript
131
+ /**
132
+ * @deprecated Use `formatRelativeDate` instead. Will be removed in v3.0.
133
+ * @see formatRelativeDate
134
+ */
135
+ export function timeAgo(date: Date): string {
136
+ return formatRelativeDate(date);
137
+ }
138
+ ```
139
+
140
+ ---
141
+
142
+ ## TODO Conventions
143
+
144
+ ### Format
145
+
146
+ ```typescript
147
+ // TODO(username): Brief description of what needs to be done
148
+ // TODO(yacin): Add pagination support when user count exceeds 1000
149
+
150
+ // TODO: Acceptable when author is obvious from git blame
151
+ // TODO: Replace with server action once Next.js supports streaming responses in actions
152
+ ```
153
+
154
+ ### Rules
155
+
156
+ - Include a name or context when the TODO is non-trivial
157
+ - Never use TODO as an excuse to leave broken code -- the code must work without the TODO being resolved
158
+ - Do not use FIXME, HACK, or XXX -- use TODO with a clear description instead
159
+ - Periodically audit TODOs and convert to issues or delete stale ones
160
+
161
+ ---
162
+
163
+ ## File-Level Comments
164
+
165
+ ### When Needed
166
+
167
+ ```typescript
168
+ /**
169
+ * Prisma client singleton for Next.js.
170
+ *
171
+ * In development, Next.js clears the Node.js module cache on every request,
172
+ * which would create a new PrismaClient instance each time. This module
173
+ * stores the client on `globalThis` to prevent connection pool exhaustion.
174
+ *
175
+ * @see https://www.prisma.io/docs/guides/nextjs
176
+ */
177
+ import { PrismaClient } from "@prisma/client";
178
+
179
+ const globalForPrisma = globalThis as unknown as { prisma: PrismaClient };
180
+
181
+ export const prisma = globalForPrisma.prisma || new PrismaClient();
182
+
183
+ if (process.env.NODE_ENV !== "production") {
184
+ globalForPrisma.prisma = prisma;
185
+ }
186
+ ```
187
+
188
+ ### When Not Needed
189
+
190
+ Do not add file-level comments to simple component files, route handlers, or utility files where the filename and exports make the purpose obvious.
191
+
192
+ ---
193
+
194
+ ## Inline Comment Style
195
+
196
+ ```typescript
197
+ // Single-line comments use double slash with a space after
198
+ // Capitalize the first word. No trailing period for short comments
199
+
200
+ // Multi-line comments that explain a complex block
201
+ // should use consecutive single-line comments rather than
202
+ // block comments (/* */). This makes it easier to toggle
203
+ // individual lines during development.
204
+
205
+ /*
206
+ * Block comments are reserved for JSDoc on exported symbols
207
+ * and file-level documentation only.
208
+ */
209
+ ```
210
+
211
+ ---
212
+
213
+ ## Anti-Patterns
214
+
215
+ | Anti-Pattern | Problem | Correct Approach |
216
+ |---|---|---|
217
+ | Commenting every line | Noise, goes stale instantly | Write self-documenting code |
218
+ | `// Fixed in PR #142` | Temporal, use git history instead | Delete; `git blame` has this info |
219
+ | Commented-out code | Confusion about what is active | Delete; git has the history |
220
+ | `// This is a hack` without explanation | Unhelpful | Explain why the hack exists and when it can be removed |
221
+ | Missing JSDoc on public API | Users guess at behavior | Document all exported functions and types |
222
+ | `@author` tags | Redundant with git blame | Let version control track authorship |
223
+
224
+ ---
225
+
226
+ _The best code reads like well-written prose. Comments are footnotes -- essential for context, distracting when overused._
@@ -0,0 +1,411 @@
1
+ # Component Patterns
2
+
3
+ Server and client component architecture for Next.js 15 App Router with composition, error boundaries, and loading states.
4
+
5
+ ---
6
+
7
+ ## Philosophy
8
+
9
+ - **Server by default**: Every component is a Server Component unless it needs interactivity
10
+ - **Push client boundaries down**: Keep `"use client"` as close to the leaves as possible
11
+ - **Composition over props**: Use children and slots instead of deeply nested prop objects
12
+ - **Explicit boundaries**: Every async operation gets a Suspense boundary, every failure gets an Error Boundary
13
+
14
+ ---
15
+
16
+ ## Server vs Client Components
17
+
18
+ ### Decision Tree
19
+
20
+ | Need | Component Type |
21
+ |---|---|
22
+ | Fetch data, access database | Server Component |
23
+ | Read from filesystem, access env vars | Server Component |
24
+ | Render static or async content | Server Component |
25
+ | useState, useEffect, useRef | Client Component (`"use client"`) |
26
+ | Event handlers (onClick, onChange) | Client Component |
27
+ | Browser APIs (localStorage, window) | Client Component |
28
+ | Third-party hooks (useForm, useSWR) | Client Component |
29
+
30
+ ### Server Component (Default)
31
+
32
+ ```typescript
33
+ // app/users/page.tsx -- Server Component (no directive needed)
34
+ import { prisma } from "@/lib/prisma";
35
+ import { UserCard } from "@/components/user-card";
36
+
37
+ export default async function UsersPage() {
38
+ const users = await prisma.user.findMany({
39
+ orderBy: { createdAt: "desc" },
40
+ take: 20,
41
+ });
42
+
43
+ return (
44
+ <main className="container py-8">
45
+ <h1 className="text-2xl font-bold">Users</h1>
46
+ <div className="mt-6 grid gap-4 sm:grid-cols-2 lg:grid-cols-3">
47
+ {users.map((user) => (
48
+ <UserCard key={user.id} user={user} />
49
+ ))}
50
+ </div>
51
+ </main>
52
+ );
53
+ }
54
+ ```
55
+
56
+ ### Client Component (Opt-In)
57
+
58
+ ```typescript
59
+ // components/like-button.tsx
60
+ "use client";
61
+
62
+ import { useState, useTransition } from "react";
63
+ import { toggleLike } from "@/actions/posts";
64
+
65
+ interface LikeButtonProps {
66
+ postId: string;
67
+ initialLiked: boolean;
68
+ initialCount: number;
69
+ }
70
+
71
+ export function LikeButton({ postId, initialLiked, initialCount }: LikeButtonProps) {
72
+ const [liked, setLiked] = useState(initialLiked);
73
+ const [count, setCount] = useState(initialCount);
74
+ const [isPending, startTransition] = useTransition();
75
+
76
+ function handleClick() {
77
+ setLiked(!liked);
78
+ setCount(liked ? count - 1 : count + 1);
79
+ startTransition(() => toggleLike(postId));
80
+ }
81
+
82
+ return (
83
+ <button
84
+ onClick={handleClick}
85
+ disabled={isPending}
86
+ className="flex items-center gap-1.5"
87
+ aria-label={liked ? "Unlike post" : "Like post"}
88
+ >
89
+ <HeartIcon filled={liked} />
90
+ <span>{count}</span>
91
+ </button>
92
+ );
93
+ }
94
+ ```
95
+
96
+ ---
97
+
98
+ ## Composition Patterns
99
+
100
+ ### Children Pattern
101
+
102
+ ```typescript
103
+ interface PageLayoutProps {
104
+ title: string;
105
+ description?: string;
106
+ actions?: React.ReactNode;
107
+ children: React.ReactNode;
108
+ }
109
+
110
+ export function PageLayout({ title, description, actions, children }: PageLayoutProps) {
111
+ return (
112
+ <div className="container py-8">
113
+ <div className="flex items-center justify-between">
114
+ <div>
115
+ <h1 className="text-2xl font-bold">{title}</h1>
116
+ {description && <p className="text-muted-foreground mt-1">{description}</p>}
117
+ </div>
118
+ {actions && <div className="flex gap-2">{actions}</div>}
119
+ </div>
120
+ <div className="mt-8">{children}</div>
121
+ </div>
122
+ );
123
+ }
124
+ ```
125
+
126
+ ### Compound Components
127
+
128
+ ```typescript
129
+ // components/card.tsx
130
+ interface CardProps {
131
+ children: React.ReactNode;
132
+ className?: string;
133
+ }
134
+
135
+ export function Card({ children, className }: CardProps) {
136
+ return <div className={cn("rounded-lg border bg-card p-6", className)}>{children}</div>;
137
+ }
138
+
139
+ function CardHeader({ children, className }: CardProps) {
140
+ return <div className={cn("mb-4", className)}>{children}</div>;
141
+ }
142
+
143
+ function CardTitle({ children, className }: { children: React.ReactNode; className?: string }) {
144
+ return <h3 className={cn("text-lg font-semibold", className)}>{children}</h3>;
145
+ }
146
+
147
+ function CardContent({ children, className }: CardProps) {
148
+ return <div className={cn("text-sm", className)}>{children}</div>;
149
+ }
150
+
151
+ Card.Header = CardHeader;
152
+ Card.Title = CardTitle;
153
+ Card.Content = CardContent;
154
+ ```
155
+
156
+ ```tsx
157
+ // Usage
158
+ <Card>
159
+ <Card.Header>
160
+ <Card.Title>Monthly Revenue</Card.Title>
161
+ </Card.Header>
162
+ <Card.Content>
163
+ <RevenueChart data={data} />
164
+ </Card.Content>
165
+ </Card>
166
+ ```
167
+
168
+ ### Render Props (Rare)
169
+
170
+ ```typescript
171
+ // Use only when children pattern is insufficient
172
+ interface DataListProps<T> {
173
+ items: T[];
174
+ renderItem: (item: T, index: number) => React.ReactNode;
175
+ emptyState?: React.ReactNode;
176
+ }
177
+
178
+ export function DataList<T>({ items, renderItem, emptyState }: DataListProps<T>) {
179
+ if (items.length === 0) {
180
+ return emptyState ?? <p className="text-muted-foreground">No items found.</p>;
181
+ }
182
+ return <ul className="divide-y">{items.map((item, i) => <li key={i}>{renderItem(item, i)}</li>)}</ul>;
183
+ }
184
+ ```
185
+
186
+ ---
187
+
188
+ ## Props Design
189
+
190
+ ### Required vs Optional
191
+
192
+ ```typescript
193
+ interface UserCardProps {
194
+ // Required: component cannot render without these
195
+ user: { id: string; name: string; email: string; avatarUrl?: string };
196
+
197
+ // Optional with defaults: common customizations
198
+ size?: "sm" | "md" | "lg";
199
+ showEmail?: boolean;
200
+
201
+ // Optional: extension points
202
+ className?: string;
203
+ actions?: React.ReactNode;
204
+ }
205
+
206
+ export function UserCard({
207
+ user,
208
+ size = "md",
209
+ showEmail = true,
210
+ className,
211
+ actions,
212
+ }: UserCardProps) {
213
+ // ...
214
+ }
215
+ ```
216
+
217
+ ### Extending HTML Elements
218
+
219
+ ```typescript
220
+ import { type ComponentProps } from "react";
221
+
222
+ interface ButtonProps extends ComponentProps<"button"> {
223
+ variant?: "primary" | "secondary" | "destructive";
224
+ size?: "sm" | "md" | "lg";
225
+ isLoading?: boolean;
226
+ }
227
+
228
+ export function Button({
229
+ variant = "primary",
230
+ size = "md",
231
+ isLoading,
232
+ disabled,
233
+ children,
234
+ className,
235
+ ...props
236
+ }: ButtonProps) {
237
+ return (
238
+ <button
239
+ className={cn(buttonVariants({ variant, size }), className)}
240
+ disabled={disabled || isLoading}
241
+ {...props}
242
+ >
243
+ {isLoading && <Spinner className="mr-2" />}
244
+ {children}
245
+ </button>
246
+ );
247
+ }
248
+ ```
249
+
250
+ ---
251
+
252
+ ## Error Boundaries
253
+
254
+ ### Route-Level Error Boundary
255
+
256
+ ```typescript
257
+ // app/dashboard/error.tsx
258
+ "use client";
259
+
260
+ interface ErrorProps {
261
+ error: Error & { digest?: string };
262
+ reset: () => void;
263
+ }
264
+
265
+ export default function DashboardError({ error, reset }: ErrorProps) {
266
+ return (
267
+ <div className="flex flex-col items-center justify-center gap-4 py-16" role="alert">
268
+ <h2 className="text-xl font-semibold">Something went wrong</h2>
269
+ <p className="text-muted-foreground max-w-md text-center">
270
+ An error occurred while loading the dashboard. Please try again.
271
+ </p>
272
+ <button onClick={reset} className="rounded-md bg-primary px-4 py-2 text-white">
273
+ Try again
274
+ </button>
275
+ </div>
276
+ );
277
+ }
278
+ ```
279
+
280
+ ### Global Error Boundary
281
+
282
+ ```typescript
283
+ // app/global-error.tsx
284
+ "use client";
285
+
286
+ export default function GlobalError({
287
+ error,
288
+ reset,
289
+ }: {
290
+ error: Error & { digest?: string };
291
+ reset: () => void;
292
+ }) {
293
+ return (
294
+ <html>
295
+ <body>
296
+ <div className="flex min-h-screen items-center justify-center">
297
+ <div className="text-center">
298
+ <h1 className="text-2xl font-bold">Application Error</h1>
299
+ <p className="mt-2">Something went wrong. Please refresh the page.</p>
300
+ <button onClick={reset} className="mt-4 rounded bg-blue-600 px-4 py-2 text-white">
301
+ Try again
302
+ </button>
303
+ </div>
304
+ </div>
305
+ </body>
306
+ </html>
307
+ );
308
+ }
309
+ ```
310
+
311
+ ---
312
+
313
+ ## Suspense and Loading States
314
+
315
+ ### Streaming with Suspense
316
+
317
+ ```typescript
318
+ // app/dashboard/page.tsx
319
+ import { Suspense } from "react";
320
+ import { RevenueChart } from "@/components/revenue-chart";
321
+ import { RecentOrders } from "@/components/recent-orders";
322
+ import { StatsSkeleton, TableSkeleton } from "@/components/skeletons";
323
+
324
+ export default function DashboardPage() {
325
+ return (
326
+ <div className="grid gap-6 lg:grid-cols-2">
327
+ <Suspense fallback={<StatsSkeleton />}>
328
+ <RevenueChart />
329
+ </Suspense>
330
+ <Suspense fallback={<TableSkeleton rows={5} />}>
331
+ <RecentOrders />
332
+ </Suspense>
333
+ </div>
334
+ );
335
+ }
336
+ ```
337
+
338
+ ### Route-Level Loading
339
+
340
+ ```typescript
341
+ // app/dashboard/loading.tsx
342
+ import { DashboardSkeleton } from "@/components/skeletons";
343
+
344
+ export default function DashboardLoading() {
345
+ return <DashboardSkeleton />;
346
+ }
347
+ ```
348
+
349
+ ### Skeleton Components
350
+
351
+ ```typescript
352
+ // components/skeletons.tsx
353
+ export function StatsSkeleton() {
354
+ return (
355
+ <div className="animate-pulse rounded-lg border p-6">
356
+ <div className="h-4 w-24 rounded bg-muted" />
357
+ <div className="mt-4 h-8 w-32 rounded bg-muted" />
358
+ </div>
359
+ );
360
+ }
361
+
362
+ export function TableSkeleton({ rows = 5 }: { rows?: number }) {
363
+ return (
364
+ <div className="animate-pulse space-y-3">
365
+ {Array.from({ length: rows }).map((_, i) => (
366
+ <div key={i} className="h-12 rounded bg-muted" />
367
+ ))}
368
+ </div>
369
+ );
370
+ }
371
+ ```
372
+
373
+ ---
374
+
375
+ ## Component File Structure
376
+
377
+ ### Single Component File
378
+
379
+ ```
380
+ components/
381
+ user-card.tsx # Component + types in one file
382
+ ```
383
+
384
+ ### Complex Component with Parts
385
+
386
+ ```
387
+ components/
388
+ data-table/
389
+ data-table.tsx # Main component
390
+ columns.tsx # Column definitions
391
+ toolbar.tsx # Filter/search toolbar
392
+ pagination.tsx # Pagination controls
393
+ index.ts # Re-export: export { DataTable } from "./data-table"
394
+ ```
395
+
396
+ ---
397
+
398
+ ## Anti-Patterns
399
+
400
+ | Anti-Pattern | Problem | Correct Approach |
401
+ |---|---|---|
402
+ | `"use client"` at the top of every file | Sends everything to the client | Default to server, opt in to client |
403
+ | Fetching data in client components | Extra round trips, no streaming | Fetch in server components, pass as props |
404
+ | Prop drilling through many layers | Fragile, hard to refactor | Composition with children/slots or context |
405
+ | God components (500+ lines) | Untestable, hard to read | Split into composed sub-components |
406
+ | Inline functions in JSX | Re-created every render | Extract to named functions or useCallback |
407
+ | Missing Suspense boundaries | All-or-nothing loading | Wrap async components individually |
408
+
409
+ ---
410
+
411
+ _Components are the atoms of your UI. Keep them small, composable, and server-rendered by default._