codingbuddy-rules 4.0.0 → 4.1.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 (34) hide show
  1. package/.ai-rules/CHANGELOG.md +42 -0
  2. package/.ai-rules/adapters/antigravity.md +1 -1
  3. package/.ai-rules/adapters/claude-code.md +79 -43
  4. package/.ai-rules/adapters/codex.md +1 -1
  5. package/.ai-rules/adapters/cursor.md +1 -1
  6. package/.ai-rules/adapters/kiro.md +1 -1
  7. package/.ai-rules/adapters/opencode.md +3 -3
  8. package/.ai-rules/adapters/q.md +1 -1
  9. package/.ai-rules/agents/README.md +1 -1
  10. package/.ai-rules/agents/accessibility-specialist.json +3 -13
  11. package/.ai-rules/agents/ai-ml-engineer.json +4 -23
  12. package/.ai-rules/agents/architecture-specialist.json +2 -9
  13. package/.ai-rules/agents/auto-mode.json +106 -0
  14. package/.ai-rules/agents/code-reviewer.json +2 -9
  15. package/.ai-rules/agents/data-engineer.json +3 -17
  16. package/.ai-rules/agents/eval-mode.json +1 -4
  17. package/.ai-rules/agents/event-architecture-specialist.json +1 -5
  18. package/.ai-rules/agents/i18n-specialist.json +1 -5
  19. package/.ai-rules/agents/migration-specialist.json +3 -15
  20. package/.ai-rules/agents/mobile-developer.json +5 -28
  21. package/.ai-rules/agents/observability-specialist.json +8 -53
  22. package/.ai-rules/agents/performance-specialist.json +3 -15
  23. package/.ai-rules/agents/plan-mode.json +1 -4
  24. package/.ai-rules/agents/platform-engineer.json +3 -13
  25. package/.ai-rules/agents/security-specialist.json +2 -8
  26. package/.ai-rules/agents/technical-planner.json +1 -4
  27. package/.ai-rules/agents/test-strategy-specialist.json +6 -36
  28. package/.ai-rules/agents/tooling-engineer.json +1 -1
  29. package/.ai-rules/checklists/accessibility.json +2 -13
  30. package/.ai-rules/checklists/security.json +2 -16
  31. package/.ai-rules/keyword-modes.json +2 -10
  32. package/.ai-rules/skills/README.md +3 -0
  33. package/.ai-rules/skills/widget-slot-architecture/SKILL.md +740 -0
  34. package/package.json +2 -2
@@ -0,0 +1,740 @@
1
+ ---
2
+ name: widget-slot-architecture
3
+ description: Architecture guide using Next.js App Router's Parallel Routes for Widget-Slot pattern. Separates static layouts from dynamic widgets to achieve separation of concerns, fault isolation, and plug-and-play development.
4
+ ---
5
+
6
+ # Widget-Slot Architecture (WSA)
7
+
8
+ ## Overview
9
+
10
+ Widget-Slot Architecture (WSA) is a frontend architecture pattern that leverages Next.js App Router's Parallel Routes feature to **strictly separate interaction logic from static layouts** and manage each feature as an independent widget unit.
11
+
12
+ **Core Principle:** Layouts handle structure only; logic is isolated in widgets.
13
+
14
+ **Requirements:** Next.js 13.4+ (App Router with Parallel Routes support)
15
+
16
+ ## When to Use
17
+
18
+ **Use this skill:**
19
+ - Designing large-scale Next.js App Router projects
20
+ - When multiple independent dynamic regions are needed within a page
21
+ - Projects requiring team-based parallel development
22
+ - Services where feature-level fault isolation is critical
23
+ - When widget reusability and portability are required
24
+
25
+ **Not needed for:**
26
+ - Simple static pages
27
+ - Small projects (over-abstraction)
28
+ - Pages Router based projects (Parallel Routes not supported)
29
+
30
+ ---
31
+
32
+ ## Philosophy (5 Core Principles)
33
+
34
+ ### 1. Separation of Concerns and Static Shell Maintenance
35
+ Layouts provide only the structural framework and contain no complex interactions. All dynamic elements are isolated in widgets.
36
+
37
+ ### 2. Independent Self-Containment
38
+ Widgets minimize external dependencies and handle their own data. A widget should work immediately when moved to a different page's slot.
39
+
40
+ ### 3. Standardized Fault Isolation
41
+ All dynamic regions (Slots) have independent Error Boundaries and Suspense. A feature's error should not cause the entire page to collapse.
42
+
43
+ ### 4. Server-Centric Data Flow
44
+ Widget communication prioritizes server data cache invalidation (`revalidateTag`) or URL state over direct state sharing.
45
+
46
+ ### 5. Plug & Play Development
47
+ Widgets are designed like Lego blocks. Developers have extreme freedom to 'add' new features to slots or 'remove' unnecessary ones without affecting business logic.
48
+
49
+ ---
50
+
51
+ ## Definitions
52
+
53
+ | Term | Definition |
54
+ |------|------------|
55
+ | **Layout** | Static structure of the service. Contains only visual elements without interactions and determines Slot positions |
56
+ | **Slot** | Dynamic region defined within a Layout. Implemented via Next.js Parallel Routes (`@slot`), managing individual loading and error states |
57
+ | **Widget** | Actual business logic unit inserted into a Slot. Includes API calls, data mutations (Actions), and user interactions (Forms, Modals, etc.) |
58
+ | **Component** | Reusable UI pieces used generically. Has no business logic or only very simple input elements |
59
+
60
+ ---
61
+
62
+ ## Folder Structure
63
+
64
+ ```
65
+ src/
66
+ ├── app/
67
+ │ ├── layout.tsx # Overall static structure definition
68
+ │ ├── @main_slot/ # [Slot] Specific position definition
69
+ │ │ ├── layout.tsx # Slot common wrapper (Suspense, ErrorBoundary)
70
+ │ │ ├── page.tsx # Widget connection (calls Widgets/MyWidget)
71
+ │ │ ├── loading.tsx # Slot-specific loading UI
72
+ │ │ ├── error.tsx # Slot-specific error UI
73
+ │ │ └── default.tsx # Placeholder when no data or route mismatch
74
+ │ └── (routes)/...
75
+ ├── Widgets/ # [Widget] Common widget storage (src/Widgets)
76
+ │ └── [WidgetName]/ # Widget folder by name (Colocation)
77
+ │ ├── index.tsx # Widget Entry Point
78
+ │ ├── ui/ # UI component folder (using barrel files)
79
+ │ ├── hooks/ # Widget-specific custom hooks
80
+ │ └── actions/ # Widget-specific Server Actions
81
+ └── Components/ # Global common UI (Atomic Design's Atoms/Molecules level)
82
+ ```
83
+
84
+ ---
85
+
86
+ ## Implementation Guide
87
+
88
+ ### 4.1 Slot Implementation (Parallel Routes)
89
+
90
+ Slots are implemented via Parallel Routes using the `@` prefix. Each Slot directly manages its own lifecycle (Loading, Error).
91
+
92
+ **Slot Layout (Error Boundary & Suspense applied):**
93
+
94
+ ```tsx
95
+ // app/@main_slot/layout.tsx
96
+ export default function MainSlotLayout({
97
+ children
98
+ }: {
99
+ children: React.ReactNode
100
+ }) {
101
+ return (
102
+ <div className="slot-container">
103
+ {/* Next.js provided loading.tsx and error.tsx wrap children */}
104
+ {children}
105
+ </div>
106
+ );
107
+ }
108
+ ```
109
+
110
+ **Slot Page (Widget connection):**
111
+
112
+ ```tsx
113
+ // app/@main_slot/page.tsx
114
+ import UserProfileWidget from "@/Widgets/UserProfile";
115
+
116
+ // Slot's page.tsx only performs the role of calling a single Widget
117
+ export default function MainSlotPage() {
118
+ return <UserProfileWidget userId="current-user" />;
119
+ }
120
+ ```
121
+
122
+ **Slot Loading:**
123
+
124
+ ```tsx
125
+ // app/@main_slot/loading.tsx
126
+ export default function MainSlotLoading() {
127
+ return <div className="animate-pulse">Loading...</div>;
128
+ }
129
+ ```
130
+
131
+ **Slot Error:**
132
+
133
+ ```tsx
134
+ // app/@main_slot/error.tsx
135
+ 'use client';
136
+
137
+ export default function MainSlotError({
138
+ error,
139
+ reset
140
+ }: {
141
+ error: Error;
142
+ reset: () => void
143
+ }) {
144
+ return (
145
+ <div className="error-container">
146
+ <p>Something went wrong in this section.</p>
147
+ <button onClick={reset}>Try again</button>
148
+ </div>
149
+ );
150
+ }
151
+ ```
152
+
153
+ **Slot Default (Fallback):**
154
+
155
+ ```tsx
156
+ // app/@main_slot/default.tsx
157
+ export default function MainSlotDefault() {
158
+ return <div>No content available</div>;
159
+ }
160
+ ```
161
+
162
+ ### 4.2 Widget Implementation (Colocation & Barrel Files)
163
+
164
+ Widgets are located within the `Widgets/` folder, with sub-elements organized by feature folders.
165
+
166
+ **Widget Entry Point:**
167
+
168
+ ```tsx
169
+ // Widgets/UserProfile/index.tsx
170
+ import { ProfileUI } from "./ui";
171
+ import { getUserData } from "./actions";
172
+
173
+ // Widgets aim to be Server Components, calling Client Component UI internally when needed
174
+ export default async function UserProfileWidget({
175
+ userId
176
+ }: {
177
+ userId: string
178
+ }) {
179
+ const userData = await getUserData(userId);
180
+
181
+ if (!userData) {
182
+ return <div>User not found.</div>;
183
+ }
184
+
185
+ return <ProfileUI data={userData} />;
186
+ }
187
+ ```
188
+
189
+ **Widget UI (Client Component):**
190
+
191
+ ```tsx
192
+ // Widgets/UserProfile/ui/ProfileUI.tsx
193
+ 'use client';
194
+
195
+ import { User } from "../types";
196
+
197
+ interface ProfileUIProps {
198
+ data: User;
199
+ }
200
+
201
+ export function ProfileUI({ data }: ProfileUIProps) {
202
+ return (
203
+ <div className="profile-card">
204
+ <h2>{data.name}</h2>
205
+ <p>{data.email}</p>
206
+ </div>
207
+ );
208
+ }
209
+ ```
210
+
211
+ **Widget UI Barrel File:**
212
+
213
+ ```tsx
214
+ // Widgets/UserProfile/ui/index.ts
215
+ export { ProfileUI } from "./ProfileUI";
216
+ export { ProfileSkeleton } from "./ProfileSkeleton";
217
+ ```
218
+
219
+ **Widget Server Actions:**
220
+
221
+ ```tsx
222
+ // Widgets/UserProfile/actions/getUserData.ts
223
+ 'use server';
224
+
225
+ import { cache } from 'react';
226
+
227
+ export const getUserData = cache(async (userId: string) => {
228
+ const res = await fetch(`/api/users/${userId}`, {
229
+ next: { tags: [`user-${userId}`] }
230
+ });
231
+
232
+ if (!res.ok) return null;
233
+ return res.json();
234
+ });
235
+ ```
236
+
237
+ **Widget Folder Structure:**
238
+
239
+ ```
240
+ Widgets/UserProfile/
241
+ ├── index.tsx # Widget Entry Point (Server Component)
242
+ ├── types.ts # Widget-specific types
243
+ ├── ui/ # UI Components (Client Components)
244
+ │ ├── index.ts # Barrel file
245
+ │ ├── ProfileUI.tsx
246
+ │ └── ProfileSkeleton.tsx
247
+ ├── hooks/ # Widget-specific hooks
248
+ │ └── useProfileEdit.ts
249
+ └── actions/ # Server Actions
250
+ ├── getUserData.ts
251
+ └── updateUserData.ts
252
+ ```
253
+
254
+ ### 4.3 Component Management and Promotion Rules
255
+
256
+ | Stage | Location | Criteria |
257
+ |-------|----------|----------|
258
+ | **Local Component** | Within widget/page `ui/` | Used temporarily only in specific widget |
259
+ | **Global Component** | `src/Components/` | Interaction-free UI reused across multiple places |
260
+ | **Widget Promotion** | `src/Widgets/` | When business logic is added or operates as independent functional unit |
261
+
262
+ **Promotion Triggers:**
263
+ - When data fetch is added to `src/Components/` elements
264
+ - When Server Action is needed
265
+ - When operating as independent functional unit (Form, complex Modal, etc.)
266
+
267
+ ### 4.4 Combining with Intercepting Routes
268
+
269
+ Parallel Routes and Intercepting Routes can be combined to implement modal patterns. Display as modal when clicking an item from a list, display as full page when accessing URL directly.
270
+
271
+ **Folder Structure:**
272
+
273
+ ```
274
+ app/
275
+ ├── layout.tsx
276
+ ├── @modal/ # Parallel Route for modals
277
+ │ ├── (.)items/[id]/ # Intercept: catch same-level route
278
+ │ │ └── page.tsx # Display as modal
279
+ │ └── default.tsx # When no modal (return null)
280
+ ├── items/
281
+ │ ├── page.tsx # List page
282
+ │ └── [id]/
283
+ │ └── page.tsx # Full page on direct access
284
+ └── page.tsx
285
+ ```
286
+
287
+ **Root Layout (including modal slot):**
288
+
289
+ ```tsx
290
+ // app/layout.tsx
291
+ export default function RootLayout({
292
+ children,
293
+ modal
294
+ }: {
295
+ children: React.ReactNode;
296
+ modal: React.ReactNode;
297
+ }) {
298
+ return (
299
+ <html>
300
+ <body>
301
+ {children}
302
+ {modal}
303
+ </body>
304
+ </html>
305
+ );
306
+ }
307
+ ```
308
+
309
+ **Intercepting Route (modal display):**
310
+
311
+ ```tsx
312
+ // app/@modal/(.)items/[id]/page.tsx
313
+ import ItemDetailWidget from "@/Widgets/ItemDetail";
314
+ import { Modal } from "@/Components/Modal";
315
+
316
+ export default function ItemModal({ params }: { params: { id: string } }) {
317
+ return (
318
+ <Modal>
319
+ <ItemDetailWidget itemId={params.id} variant="compact" />
320
+ </Modal>
321
+ );
322
+ }
323
+ ```
324
+
325
+ **Modal Default (when modal inactive):**
326
+
327
+ ```tsx
328
+ // app/@modal/default.tsx
329
+ export default function ModalDefault() {
330
+ return null; // Render nothing when no modal
331
+ }
332
+ ```
333
+
334
+ **Intercepting Route Rules:**
335
+
336
+ | Pattern | Description |
337
+ |---------|-------------|
338
+ | `(.)` | Intercept same-level segment |
339
+ | `(..)` | Intercept one level up segment |
340
+ | `(..)(..)` | Intercept two levels up segment |
341
+ | `(...)` | Intercept from root (`app`) |
342
+
343
+ ---
344
+
345
+ ## Core Rules (5 Key Rules)
346
+
347
+ ### Rule 1: One Slot, One Widget
348
+ A `@slot`'s `page.tsx` should import and return **only one Widget** in principle.
349
+
350
+ ```tsx
351
+ // ✅ Good
352
+ export default function SlotPage() {
353
+ return <UserProfileWidget userId="123" />;
354
+ }
355
+
356
+ // ❌ Bad - Multiple widgets mixed
357
+ export default function SlotPage() {
358
+ return (
359
+ <>
360
+ <UserProfileWidget />
361
+ <UserSettingsWidget />
362
+ </>
363
+ );
364
+ }
365
+ ```
366
+
367
+ ### Rule 2: Data Self-Sufficiency
368
+ Widgets are recommended to **fetch required data internally**. Only receive minimal props like identifiers (IDs) from parent (Layout).
369
+
370
+ ```tsx
371
+ // ✅ Good - Receive only ID, fetch data inside widget
372
+ export default async function OrderWidget({ orderId }: { orderId: string }) {
373
+ const order = await getOrderData(orderId);
374
+ return <OrderUI order={order} />;
375
+ }
376
+
377
+ // ❌ Bad - Parent passes data
378
+ export default function OrderWidget({ order }: { order: Order }) {
379
+ return <OrderUI order={order} />;
380
+ }
381
+ ```
382
+
383
+ ### Rule 3: Minimize Communication
384
+ Avoid direct state sharing between widgets. When changing data, use **`revalidateTag`** to refresh server state or design other widgets to react via **URL Query String**.
385
+
386
+ ```tsx
387
+ // ✅ Good - Cache invalidation via Server Action
388
+ 'use server';
389
+ import { revalidateTag } from 'next/cache';
390
+
391
+ export async function updateUser(userId: string, data: UserInput) {
392
+ await db.users.update(userId, data);
393
+ revalidateTag(`user-${userId}`); // Related widgets auto re-fetch
394
+ }
395
+
396
+ // ✅ Good - Inter-widget communication via URL state
397
+ // /dashboard?selectedUser=123
398
+ // UserListWidget changes URL → UserDetailWidget reacts via searchParams
399
+ ```
400
+
401
+ ### Rule 4: Ensure Default State
402
+ `default.tsx` must always be defined to return appropriate messages or Null UI so layouts don't break when there's no data.
403
+
404
+ ```tsx
405
+ // app/@sidebar/default.tsx
406
+ export default function SidebarDefault() {
407
+ return (
408
+ <aside className="sidebar-placeholder">
409
+ <p>Select an item to view details</p>
410
+ </aside>
411
+ );
412
+ }
413
+ ```
414
+
415
+ ### Rule 5: Widgets Must Be Portable
416
+ Widgets should work immediately without additional modifications when moved to a different page's slot.
417
+
418
+ ```tsx
419
+ // app/dashboard/@profile/page.tsx
420
+ import UserProfileWidget from "@/Widgets/UserProfile";
421
+ export default function DashboardProfileSlot() {
422
+ return <UserProfileWidget userId="current-user" />;
423
+ }
424
+
425
+ // app/admin/@profile/page.tsx - Reuse same widget in different slot
426
+ import UserProfileWidget from "@/Widgets/UserProfile";
427
+ export default function AdminProfileSlot({ params }: { params: { userId: string } }) {
428
+ return <UserProfileWidget userId={params.userId} />;
429
+ }
430
+ ```
431
+
432
+ ---
433
+
434
+ ## Checklist
435
+
436
+ ### Design Phase
437
+ - [ ] Dynamic regions (Slots) in page identified
438
+ - [ ] Widgets corresponding to each Slot defined
439
+ - [ ] Data dependencies between Widgets analyzed
440
+ - [ ] URL state vs Server State usage decided
441
+
442
+ ### Implementation Phase
443
+ - [ ] `@slot/` directory structure created (layout, page, loading, error, default)
444
+ - [ ] Widgets colocated in `Widgets/[Name]/` structure
445
+ - [ ] Widgets start as Server Components, separate Client Components when needed
446
+ - [ ] Server Actions separated in `actions/` folder
447
+ - [ ] UI components managed in `ui/` folder with barrel files
448
+
449
+ ### Verification Phase
450
+ - [ ] Each Slot has independent loading/error states verified
451
+ - [ ] Widgets work when moved to different Slots verified
452
+ - [ ] One Widget's error doesn't affect other regions verified
453
+ - [ ] `default.tsx` defined for all Slots verified
454
+
455
+ ---
456
+
457
+ ## Testing Strategy
458
+
459
+ Widgets must be independently testable. Test Server Component Widgets and Client Component UIs separately.
460
+
461
+ ### Widget Test Structure
462
+
463
+ ```
464
+ Widgets/UserProfile/
465
+ ├── __tests__/
466
+ │ ├── UserProfile.test.tsx # Widget integration test
467
+ │ └── ui/
468
+ │ └── ProfileUI.test.tsx # UI component unit test
469
+ ├── index.tsx
470
+ └── ui/
471
+ └── ProfileUI.tsx
472
+ ```
473
+
474
+ ### Server Component Widget Testing
475
+
476
+ Mock APIs with MSW (Mock Service Worker) and call Server Components directly.
477
+
478
+ ```tsx
479
+ // Widgets/UserProfile/__tests__/UserProfile.test.tsx
480
+ import { render, screen } from '@testing-library/react';
481
+ import { setupServer } from 'msw/node';
482
+ import { http, HttpResponse } from 'msw';
483
+ import UserProfileWidget from '../index';
484
+
485
+ const server = setupServer(
486
+ http.get('/api/users/:id', ({ params }) => {
487
+ return HttpResponse.json({
488
+ id: params.id,
489
+ name: 'Test User',
490
+ email: 'test@example.com'
491
+ });
492
+ })
493
+ );
494
+
495
+ beforeAll(() => server.listen());
496
+ afterEach(() => server.resetHandlers());
497
+ afterAll(() => server.close());
498
+
499
+ describe('UserProfileWidget', () => {
500
+ it('renders user data correctly', async () => {
501
+ // Render by directly awaiting Server Component
502
+ const Component = await UserProfileWidget({ userId: '123' });
503
+ render(Component);
504
+
505
+ expect(await screen.findByText('Test User')).toBeInTheDocument();
506
+ expect(screen.getByText('test@example.com')).toBeInTheDocument();
507
+ });
508
+
509
+ it('shows not found message for invalid user', async () => {
510
+ server.use(
511
+ http.get('/api/users/:id', () => {
512
+ return new HttpResponse(null, { status: 404 });
513
+ })
514
+ );
515
+
516
+ const Component = await UserProfileWidget({ userId: 'invalid' });
517
+ render(Component);
518
+
519
+ expect(screen.getByText('User not found.')).toBeInTheDocument();
520
+ });
521
+ });
522
+ ```
523
+
524
+ ### Client Component UI Testing
525
+
526
+ Test UI components independently based on props.
527
+
528
+ ```tsx
529
+ // Widgets/UserProfile/__tests__/ui/ProfileUI.test.tsx
530
+ import { render, screen, fireEvent } from '@testing-library/react';
531
+ import { ProfileUI } from '../../ui/ProfileUI';
532
+
533
+ describe('ProfileUI', () => {
534
+ const mockUser = {
535
+ id: '1',
536
+ name: 'John Doe',
537
+ email: 'john@example.com'
538
+ };
539
+
540
+ it('displays user information', () => {
541
+ render(<ProfileUI data={mockUser} />);
542
+
543
+ expect(screen.getByText('John Doe')).toBeInTheDocument();
544
+ expect(screen.getByText('john@example.com')).toBeInTheDocument();
545
+ });
546
+
547
+ it('handles edit button click', () => {
548
+ const onEdit = vi.fn();
549
+ render(<ProfileUI data={mockUser} onEdit={onEdit} />);
550
+
551
+ fireEvent.click(screen.getByRole('button', { name: /edit/i }));
552
+ expect(onEdit).toHaveBeenCalledWith(mockUser.id);
553
+ });
554
+ });
555
+ ```
556
+
557
+ ### Server Action Testing
558
+
559
+ Test Server Actions by calling them directly.
560
+
561
+ ```tsx
562
+ // Widgets/UserProfile/__tests__/actions/updateUser.test.ts
563
+ import { updateUser } from '../../actions/updateUser';
564
+ import { revalidateTag } from 'next/cache';
565
+
566
+ vi.mock('next/cache', () => ({
567
+ revalidateTag: vi.fn()
568
+ }));
569
+
570
+ describe('updateUser action', () => {
571
+ it('updates user and revalidates cache', async () => {
572
+ const result = await updateUser('123', { name: 'Updated Name' });
573
+
574
+ expect(result.success).toBe(true);
575
+ expect(revalidateTag).toHaveBeenCalledWith('user-123');
576
+ });
577
+ });
578
+ ```
579
+
580
+ ### Testing Best Practices
581
+
582
+ | Practice | Description |
583
+ |----------|-------------|
584
+ | **Widget Isolation** | Each Widget must be independently testable |
585
+ | **API Mocking** | Mock API responses with MSW, no real server calls |
586
+ | **User-Centric** | Test from user perspective with Testing Library |
587
+ | **No Implementation Details** | Test behavior, not internal implementation |
588
+ | **Minimize Snapshots** | Use snapshot tests only for essential UI |
589
+
590
+ ---
591
+
592
+ ## Expected Benefits
593
+
594
+ | Benefit | Description |
595
+ |---------|-------------|
596
+ | **Parallelism** | Developers work on independent widgets per slot, minimizing conflicts |
597
+ | **Readability** | Page structure visible from layout files, actual logic cohesive in widget folders |
598
+ | **Flexibility** | Moving or replacing widgets is very simple when requirements change |
599
+ | **Fault Isolation** | One widget's error doesn't bring down the entire page |
600
+ | **Performance** | Independent loading possible via Parallel Routes streaming |
601
+
602
+ ---
603
+
604
+ ## Common Mistakes
605
+
606
+ | Mistake | Fix |
607
+ |---------|-----|
608
+ | Business logic in Layout | Move all logic to Widgets |
609
+ | Excessive props to Widget | Pass only ID, fetch data inside Widget |
610
+ | Direct state sharing | Use `revalidateTag` or URL params |
611
+ | Missing `default.tsx` | Required for all Slots |
612
+ | Multiple Widgets in one Slot | Follow one slot, one widget principle |
613
+ | Using Client Component as Widget | Prioritize Server Component, separate only UI as Client |
614
+
615
+ ---
616
+
617
+ ## Architecture Diagram
618
+
619
+ ### Overall Structure
620
+
621
+ ```mermaid
622
+ graph TB
623
+ subgraph "app/ (Next.js App Router)"
624
+ Layout[layout.tsx<br/>Static Shell]
625
+
626
+ subgraph "@slot_a"
627
+ SlotA_Layout[layout.tsx]
628
+ SlotA_Page[page.tsx]
629
+ SlotA_Loading[loading.tsx]
630
+ SlotA_Error[error.tsx]
631
+ end
632
+
633
+ subgraph "@slot_b"
634
+ SlotB_Layout[layout.tsx]
635
+ SlotB_Page[page.tsx]
636
+ SlotB_Loading[loading.tsx]
637
+ SlotB_Error[error.tsx]
638
+ end
639
+ end
640
+
641
+ subgraph "Widgets/"
642
+ WidgetA[WidgetA/index.tsx<br/>Server Component]
643
+ WidgetB[WidgetB/index.tsx<br/>Server Component]
644
+
645
+ subgraph "Widget Internal"
646
+ UI[ui/<br/>Client Components]
647
+ Actions[actions/<br/>Server Actions]
648
+ Hooks[hooks/<br/>Custom Hooks]
649
+ end
650
+ end
651
+
652
+ Layout --> SlotA_Layout
653
+ Layout --> SlotB_Layout
654
+ SlotA_Page --> WidgetA
655
+ SlotB_Page --> WidgetB
656
+ WidgetA --> UI
657
+ WidgetA --> Actions
658
+ ```
659
+
660
+ ### Data Flow
661
+
662
+ ```mermaid
663
+ sequenceDiagram
664
+ participant User
665
+ participant SlotPage as @slot/page.tsx
666
+ participant Widget as Widget (Server)
667
+ participant Action as Server Action
668
+ participant API as API/Database
669
+ participant UI as UI (Client)
670
+
671
+ User->>SlotPage: Page access
672
+ SlotPage->>Widget: Pass ID props
673
+ Widget->>API: Data fetch (with tags)
674
+ API-->>Widget: Return data
675
+ Widget->>UI: Pass data props
676
+ UI-->>User: Render UI
677
+
678
+ User->>UI: User action (click)
679
+ UI->>Action: Call Server Action
680
+ Action->>API: Update data
681
+ Action->>Action: revalidateTag()
682
+ Note over Widget,UI: Auto re-fetch<br/>via cache invalidation
683
+ Widget->>API: Re-request (auto)
684
+ API-->>Widget: New data
685
+ Widget->>UI: Updated UI
686
+ ```
687
+
688
+ ### Inter-Widget Communication Patterns
689
+
690
+ ```mermaid
691
+ flowchart LR
692
+ subgraph "Pattern 1: Server Cache"
693
+ W1[Widget A] -->|revalidateTag| Cache[(Server Cache)]
694
+ Cache -->|auto re-fetch| W2[Widget B]
695
+ end
696
+
697
+ subgraph "Pattern 2: URL State"
698
+ W3[Widget C] -->|router.push| URL[URL Params]
699
+ URL -->|searchParams| W4[Widget D]
700
+ end
701
+ ```
702
+
703
+ ---
704
+
705
+ ## Quick Reference
706
+
707
+ ```
708
+ WSA Structure
709
+ ────────────────────────────────────
710
+ app/
711
+ ├── layout.tsx # Static shell (no logic)
712
+ ├── @slot/
713
+ │ ├── layout.tsx # Slot wrapper
714
+ │ ├── page.tsx # Widget connector
715
+ │ ├── loading.tsx # Loading UI
716
+ │ ├── error.tsx # Error UI
717
+ │ └── default.tsx # Fallback UI
718
+
719
+ Widgets/
720
+ └── [Name]/
721
+ ├── index.tsx # Entry (Server Component)
722
+ ├── ui/ # Client Components
723
+ ├── actions/ # Server Actions
724
+ └── hooks/ # Custom hooks
725
+
726
+ Data Flow
727
+ ────────────────────────────────────
728
+ Parent → Widget : ID only (minimal props)
729
+ Widget → Data : Internal fetch (self-contained)
730
+ Widget ↔ Widget : revalidateTag / URL params
731
+ Widget → Server : Server Actions
732
+
733
+ Key Rules
734
+ ────────────────────────────────────
735
+ 1. One Slot = One Widget
736
+ 2. Data Self-Sufficiency
737
+ 3. Minimize Direct Communication
738
+ 4. Always Define default.tsx
739
+ 5. Widgets Must Be Portable
740
+ ```