omgkit 2.2.0 → 2.3.1

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 (60) hide show
  1. package/README.md +3 -3
  2. package/package.json +1 -1
  3. package/plugin/skills/databases/database-management/SKILL.md +288 -0
  4. package/plugin/skills/databases/database-migration/SKILL.md +285 -0
  5. package/plugin/skills/databases/database-schema-design/SKILL.md +195 -0
  6. package/plugin/skills/databases/mongodb/SKILL.md +60 -776
  7. package/plugin/skills/databases/prisma/SKILL.md +53 -744
  8. package/plugin/skills/databases/redis/SKILL.md +53 -860
  9. package/plugin/skills/databases/supabase/SKILL.md +283 -0
  10. package/plugin/skills/devops/aws/SKILL.md +68 -672
  11. package/plugin/skills/devops/github-actions/SKILL.md +54 -657
  12. package/plugin/skills/devops/kubernetes/SKILL.md +67 -602
  13. package/plugin/skills/devops/performance-profiling/SKILL.md +59 -863
  14. package/plugin/skills/frameworks/django/SKILL.md +87 -853
  15. package/plugin/skills/frameworks/express/SKILL.md +95 -1301
  16. package/plugin/skills/frameworks/fastapi/SKILL.md +90 -1198
  17. package/plugin/skills/frameworks/laravel/SKILL.md +87 -1187
  18. package/plugin/skills/frameworks/nestjs/SKILL.md +106 -973
  19. package/plugin/skills/frameworks/react/SKILL.md +94 -962
  20. package/plugin/skills/frameworks/vue/SKILL.md +95 -1242
  21. package/plugin/skills/frontend/accessibility/SKILL.md +91 -1056
  22. package/plugin/skills/frontend/frontend-design/SKILL.md +69 -1262
  23. package/plugin/skills/frontend/responsive/SKILL.md +76 -799
  24. package/plugin/skills/frontend/shadcn-ui/SKILL.md +73 -921
  25. package/plugin/skills/frontend/tailwindcss/SKILL.md +60 -788
  26. package/plugin/skills/frontend/threejs/SKILL.md +72 -1266
  27. package/plugin/skills/languages/javascript/SKILL.md +106 -849
  28. package/plugin/skills/methodology/brainstorming/SKILL.md +70 -576
  29. package/plugin/skills/methodology/defense-in-depth/SKILL.md +79 -831
  30. package/plugin/skills/methodology/dispatching-parallel-agents/SKILL.md +81 -654
  31. package/plugin/skills/methodology/executing-plans/SKILL.md +86 -529
  32. package/plugin/skills/methodology/finishing-development-branch/SKILL.md +95 -586
  33. package/plugin/skills/methodology/problem-solving/SKILL.md +67 -681
  34. package/plugin/skills/methodology/receiving-code-review/SKILL.md +70 -533
  35. package/plugin/skills/methodology/requesting-code-review/SKILL.md +70 -610
  36. package/plugin/skills/methodology/root-cause-tracing/SKILL.md +70 -646
  37. package/plugin/skills/methodology/sequential-thinking/SKILL.md +70 -478
  38. package/plugin/skills/methodology/systematic-debugging/SKILL.md +66 -559
  39. package/plugin/skills/methodology/test-driven-development/SKILL.md +91 -752
  40. package/plugin/skills/methodology/testing-anti-patterns/SKILL.md +78 -687
  41. package/plugin/skills/methodology/token-optimization/SKILL.md +72 -602
  42. package/plugin/skills/methodology/verification-before-completion/SKILL.md +108 -529
  43. package/plugin/skills/methodology/writing-plans/SKILL.md +79 -566
  44. package/plugin/skills/omega/omega-architecture/SKILL.md +91 -752
  45. package/plugin/skills/omega/omega-coding/SKILL.md +161 -552
  46. package/plugin/skills/omega/omega-sprint/SKILL.md +132 -777
  47. package/plugin/skills/omega/omega-testing/SKILL.md +157 -845
  48. package/plugin/skills/omega/omega-thinking/SKILL.md +165 -606
  49. package/plugin/skills/security/better-auth/SKILL.md +46 -1034
  50. package/plugin/skills/security/oauth/SKILL.md +80 -934
  51. package/plugin/skills/security/owasp/SKILL.md +78 -862
  52. package/plugin/skills/testing/playwright/SKILL.md +77 -700
  53. package/plugin/skills/testing/pytest/SKILL.md +73 -811
  54. package/plugin/skills/testing/vitest/SKILL.md +60 -920
  55. package/plugin/skills/tools/document-processing/SKILL.md +111 -838
  56. package/plugin/skills/tools/image-processing/SKILL.md +126 -659
  57. package/plugin/skills/tools/mcp-development/SKILL.md +85 -758
  58. package/plugin/skills/tools/media-processing/SKILL.md +118 -735
  59. package/plugin/stdrules/SKILL_STANDARDS.md +490 -0
  60. package/plugin/skills/SKILL_STANDARDS.md +0 -743
@@ -1,1035 +1,167 @@
1
1
  ---
2
- name: react
3
- description: React development with modern patterns, hooks, state management, and performance optimization
4
- category: frameworks
5
- triggers:
6
- - react
7
- - react component
8
- - react hooks
9
- - useState
10
- - useEffect
11
- - jsx
12
- - tsx
13
- - react app
2
+ name: building-react-apps
3
+ description: Builds production React applications with hooks, TypeScript, state management, and performance optimization. Use when creating React SPAs, component systems, or interactive UIs.
14
4
  ---
15
5
 
16
6
  # React
17
7
 
18
- Modern **React development** following industry best practices. This skill covers functional components, hooks, state management, performance optimization, and testing patterns used by top engineering teams.
19
-
20
- ## Purpose
21
-
22
- Build maintainable React applications with confidence:
23
-
24
- - Write clean, reusable functional components
25
- - Master React hooks for state and side effects
26
- - Implement efficient state management patterns
27
- - Optimize performance with memoization
28
- - Handle forms and validation elegantly
29
- - Create accessible, responsive UIs
30
- - Test components effectively
31
-
32
- ## Features
33
-
34
- ### 1. Functional Components with TypeScript
8
+ ## Quick Start
35
9
 
36
10
  ```tsx
37
- // Well-typed component with props interface
38
- interface ButtonProps {
39
- variant?: 'primary' | 'secondary' | 'danger';
40
- size?: 'sm' | 'md' | 'lg';
41
- disabled?: boolean;
42
- loading?: boolean;
43
- onClick?: () => void;
44
- children: React.ReactNode;
45
- }
11
+ import { useState } from 'react';
46
12
 
47
- export function Button({
48
- variant = 'primary',
49
- size = 'md',
50
- disabled = false,
51
- loading = false,
52
- onClick,
53
- children,
54
- }: ButtonProps) {
55
- const baseStyles = 'rounded font-medium transition-colors focus:outline-none focus:ring-2';
56
-
57
- const variantStyles = {
58
- primary: 'bg-blue-600 text-white hover:bg-blue-700 focus:ring-blue-500',
59
- secondary: 'bg-gray-200 text-gray-900 hover:bg-gray-300 focus:ring-gray-500',
60
- danger: 'bg-red-600 text-white hover:bg-red-700 focus:ring-red-500',
61
- };
62
-
63
- const sizeStyles = {
64
- sm: 'px-3 py-1.5 text-sm',
65
- md: 'px-4 py-2 text-base',
66
- lg: 'px-6 py-3 text-lg',
67
- };
13
+ function Counter() {
14
+ const [count, setCount] = useState(0);
68
15
 
69
16
  return (
70
- <button
71
- type="button"
72
- disabled={disabled || loading}
73
- onClick={onClick}
74
- className={`${baseStyles} ${variantStyles[variant]} ${sizeStyles[size]} ${
75
- disabled || loading ? 'opacity-50 cursor-not-allowed' : ''
76
- }`}
77
- >
78
- {loading ? (
79
- <span className="flex items-center gap-2">
80
- <Spinner size={size} />
81
- Loading...
82
- </span>
83
- ) : (
84
- children
85
- )}
17
+ <button onClick={() => setCount(c => c + 1)}>
18
+ Count: {count}
86
19
  </button>
87
20
  );
88
21
  }
89
-
90
- // Card component with composition pattern
91
- interface CardProps {
92
- children: React.ReactNode;
93
- className?: string;
94
- }
95
-
96
- interface CardHeaderProps {
97
- title: string;
98
- subtitle?: string;
99
- action?: React.ReactNode;
100
- }
101
-
102
- export function Card({ children, className = '' }: CardProps) {
103
- return (
104
- <div className={`bg-white rounded-lg shadow-md overflow-hidden ${className}`}>
105
- {children}
106
- </div>
107
- );
108
- }
109
-
110
- Card.Header = function CardHeader({ title, subtitle, action }: CardHeaderProps) {
111
- return (
112
- <div className="px-6 py-4 border-b border-gray-200 flex justify-between items-center">
113
- <div>
114
- <h3 className="text-lg font-semibold text-gray-900">{title}</h3>
115
- {subtitle && <p className="text-sm text-gray-500">{subtitle}</p>}
116
- </div>
117
- {action && <div>{action}</div>}
118
- </div>
119
- );
120
- };
121
-
122
- Card.Body = function CardBody({ children, className = '' }: CardProps) {
123
- return <div className={`px-6 py-4 ${className}`}>{children}</div>;
124
- };
125
-
126
- Card.Footer = function CardFooter({ children, className = '' }: CardProps) {
127
- return (
128
- <div className={`px-6 py-4 bg-gray-50 border-t border-gray-200 ${className}`}>
129
- {children}
130
- </div>
131
- );
132
- };
133
22
  ```
134
23
 
135
- ### 2. Essential Hooks Patterns
136
-
137
- ```tsx
138
- import { useState, useEffect, useCallback, useMemo, useRef } from 'react';
139
-
140
- // useState with complex state
141
- interface FormState {
142
- name: string;
143
- email: string;
144
- errors: Record<string, string>;
145
- isSubmitting: boolean;
146
- }
147
-
148
- function useForm(initialValues: Partial<FormState>) {
149
- const [state, setState] = useState<FormState>({
150
- name: '',
151
- email: '',
152
- errors: {},
153
- isSubmitting: false,
154
- ...initialValues,
155
- });
156
-
157
- const setField = useCallback((field: keyof FormState, value: unknown) => {
158
- setState(prev => ({ ...prev, [field]: value }));
159
- }, []);
160
-
161
- const setError = useCallback((field: string, error: string) => {
162
- setState(prev => ({
163
- ...prev,
164
- errors: { ...prev.errors, [field]: error },
165
- }));
166
- }, []);
167
-
168
- const clearErrors = useCallback(() => {
169
- setState(prev => ({ ...prev, errors: {} }));
170
- }, []);
171
-
172
- return { state, setField, setError, clearErrors };
173
- }
174
-
175
- // useEffect patterns
176
- function UserProfile({ userId }: { userId: string }) {
177
- const [user, setUser] = useState<User | null>(null);
178
- const [loading, setLoading] = useState(true);
179
- const [error, setError] = useState<Error | null>(null);
180
-
181
- useEffect(() => {
182
- let cancelled = false;
183
-
184
- async function fetchUser() {
185
- setLoading(true);
186
- setError(null);
187
-
188
- try {
189
- const response = await fetch(`/api/users/${userId}`);
190
- if (!response.ok) throw new Error('Failed to fetch user');
191
-
192
- const data = await response.json();
193
- if (!cancelled) {
194
- setUser(data);
195
- }
196
- } catch (err) {
197
- if (!cancelled) {
198
- setError(err instanceof Error ? err : new Error('Unknown error'));
199
- }
200
- } finally {
201
- if (!cancelled) {
202
- setLoading(false);
203
- }
204
- }
205
- }
206
-
207
- fetchUser();
208
-
209
- // Cleanup function prevents state updates after unmount
210
- return () => {
211
- cancelled = true;
212
- };
213
- }, [userId]);
214
-
215
- if (loading) return <LoadingSpinner />;
216
- if (error) return <ErrorMessage error={error} />;
217
- if (!user) return null;
218
-
219
- return <UserCard user={user} />;
220
- }
221
-
222
- // useCallback for stable function references
223
- function SearchResults({ query }: { query: string }) {
224
- const [results, setResults] = useState<SearchResult[]>([]);
225
-
226
- // Memoize the search function to prevent unnecessary re-fetches
227
- const performSearch = useCallback(async (searchQuery: string) => {
228
- if (!searchQuery.trim()) {
229
- setResults([]);
230
- return;
231
- }
232
-
233
- const response = await fetch(`/api/search?q=${encodeURIComponent(searchQuery)}`);
234
- const data = await response.json();
235
- setResults(data.results);
236
- }, []);
237
-
238
- // Debounce search with useEffect
239
- useEffect(() => {
240
- const timeoutId = setTimeout(() => {
241
- performSearch(query);
242
- }, 300);
243
-
244
- return () => clearTimeout(timeoutId);
245
- }, [query, performSearch]);
24
+ ## Features
246
25
 
247
- return (
248
- <ul>
249
- {results.map(result => (
250
- <SearchResultItem key={result.id} result={result} />
251
- ))}
252
- </ul>
253
- );
254
- }
26
+ | Feature | Description | Guide |
27
+ |---------|-------------|-------|
28
+ | Hooks | useState, useEffect, custom hooks | [HOOKS.md](HOOKS.md) |
29
+ | TypeScript | Props, state, event typing | [TYPESCRIPT.md](TYPESCRIPT.md) |
30
+ | State | Zustand, Context, useReducer | [STATE.md](STATE.md) |
31
+ | Forms | React Hook Form, Zod validation | [FORMS.md](FORMS.md) |
32
+ | Testing | Vitest, Testing Library | [TESTING.md](TESTING.md) |
33
+ | Performance | memo, useMemo, useCallback, Suspense | [PERFORMANCE.md](PERFORMANCE.md) |
255
34
 
256
- // useMemo for expensive computations
257
- function DataTable({ data, filters }: { data: DataItem[]; filters: Filters }) {
258
- // Only recompute when data or filters change
259
- const filteredData = useMemo(() => {
260
- return data
261
- .filter(item => {
262
- if (filters.status && item.status !== filters.status) return false;
263
- if (filters.search && !item.name.toLowerCase().includes(filters.search.toLowerCase())) {
264
- return false;
265
- }
266
- return true;
267
- })
268
- .sort((a, b) => {
269
- const direction = filters.sortDirection === 'asc' ? 1 : -1;
270
- return a[filters.sortBy] > b[filters.sortBy] ? direction : -direction;
271
- });
272
- }, [data, filters]);
35
+ ## Common Patterns
273
36
 
274
- const stats = useMemo(() => ({
275
- total: filteredData.length,
276
- active: filteredData.filter(d => d.status === 'active').length,
277
- inactive: filteredData.filter(d => d.status === 'inactive').length,
278
- }), [filteredData]);
37
+ ### Typed Component with Props
279
38
 
280
- return (
281
- <div>
282
- <StatsBar stats={stats} />
283
- <Table data={filteredData} />
284
- </div>
285
- );
39
+ ```tsx
40
+ interface UserCardProps {
41
+ user: { id: string; name: string; email: string };
42
+ onEdit: (user: UserCardProps['user']) => void;
43
+ onDelete: (id: string) => void;
286
44
  }
287
45
 
288
- // useRef for DOM access and mutable values
289
- function AutoFocusInput() {
290
- const inputRef = useRef<HTMLInputElement>(null);
291
- const renderCount = useRef(0);
292
-
293
- useEffect(() => {
294
- // Focus input on mount
295
- inputRef.current?.focus();
296
- }, []);
297
-
298
- useEffect(() => {
299
- // Track renders without causing re-renders
300
- renderCount.current += 1;
301
- });
302
-
46
+ function UserCard({ user, onEdit, onDelete }: UserCardProps) {
303
47
  return (
304
- <div>
305
- <input ref={inputRef} placeholder="I'm auto-focused" />
306
- <p>Render count: {renderCount.current}</p>
48
+ <div className="user-card">
49
+ <h3>{user.name}</h3>
50
+ <p>{user.email}</p>
51
+ <button onClick={() => onEdit(user)}>Edit</button>
52
+ <button onClick={() => onDelete(user.id)}>Delete</button>
307
53
  </div>
308
54
  );
309
55
  }
310
56
  ```
311
57
 
312
- ### 3. Custom Hooks
58
+ ### Custom Hook for Data Fetching
313
59
 
314
60
  ```tsx
315
- // Data fetching hook with error handling and refetch
316
- interface UseFetchResult<T> {
317
- data: T | null;
318
- loading: boolean;
319
- error: Error | null;
320
- refetch: () => Promise<void>;
321
- }
322
-
323
- function useFetch<T>(url: string, options?: RequestInit): UseFetchResult<T> {
61
+ function useFetch<T>(url: string) {
324
62
  const [data, setData] = useState<T | null>(null);
325
63
  const [loading, setLoading] = useState(true);
326
64
  const [error, setError] = useState<Error | null>(null);
327
65
 
328
- const fetchData = useCallback(async () => {
329
- setLoading(true);
330
- setError(null);
331
-
332
- try {
333
- const response = await fetch(url, options);
334
- if (!response.ok) {
335
- throw new Error(`HTTP error! status: ${response.status}`);
336
- }
337
- const json = await response.json();
338
- setData(json);
339
- } catch (e) {
340
- setError(e instanceof Error ? e : new Error('Fetch failed'));
341
- } finally {
342
- setLoading(false);
343
- }
344
- }, [url, options]);
345
-
346
- useEffect(() => {
347
- fetchData();
348
- }, [fetchData]);
349
-
350
- return { data, loading, error, refetch: fetchData };
351
- }
352
-
353
- // Local storage hook with SSR support
354
- function useLocalStorage<T>(key: string, initialValue: T) {
355
- const [storedValue, setStoredValue] = useState<T>(() => {
356
- if (typeof window === 'undefined') {
357
- return initialValue;
358
- }
359
-
360
- try {
361
- const item = window.localStorage.getItem(key);
362
- return item ? JSON.parse(item) : initialValue;
363
- } catch (error) {
364
- console.warn(`Error reading localStorage key "${key}":`, error);
365
- return initialValue;
366
- }
367
- });
368
-
369
- const setValue = useCallback((value: T | ((val: T) => T)) => {
370
- try {
371
- const valueToStore = value instanceof Function ? value(storedValue) : value;
372
- setStoredValue(valueToStore);
373
-
374
- if (typeof window !== 'undefined') {
375
- window.localStorage.setItem(key, JSON.stringify(valueToStore));
376
- }
377
- } catch (error) {
378
- console.warn(`Error setting localStorage key "${key}":`, error);
379
- }
380
- }, [key, storedValue]);
381
-
382
- return [storedValue, setValue] as const;
383
- }
384
-
385
- // Debounced value hook
386
- function useDebounce<T>(value: T, delay: number): T {
387
- const [debouncedValue, setDebouncedValue] = useState(value);
388
-
389
- useEffect(() => {
390
- const timer = setTimeout(() => {
391
- setDebouncedValue(value);
392
- }, delay);
393
-
394
- return () => clearTimeout(timer);
395
- }, [value, delay]);
396
-
397
- return debouncedValue;
398
- }
399
-
400
- // Window size hook
401
- function useWindowSize() {
402
- const [size, setSize] = useState({
403
- width: typeof window !== 'undefined' ? window.innerWidth : 0,
404
- height: typeof window !== 'undefined' ? window.innerHeight : 0,
405
- });
406
-
407
- useEffect(() => {
408
- function handleResize() {
409
- setSize({
410
- width: window.innerWidth,
411
- height: window.innerHeight,
412
- });
413
- }
414
-
415
- window.addEventListener('resize', handleResize);
416
- return () => window.removeEventListener('resize', handleResize);
417
- }, []);
418
-
419
- return size;
420
- }
421
-
422
- // Previous value hook
423
- function usePrevious<T>(value: T): T | undefined {
424
- const ref = useRef<T>();
425
-
426
66
  useEffect(() => {
427
- ref.current = value;
428
- }, [value]);
429
-
430
- return ref.current;
431
- }
67
+ let cancelled = false;
432
68
 
433
- // Click outside hook
434
- function useClickOutside(ref: React.RefObject<HTMLElement>, handler: () => void) {
435
- useEffect(() => {
436
- function handleClickOutside(event: MouseEvent) {
437
- if (ref.current && !ref.current.contains(event.target as Node)) {
438
- handler();
69
+ async function fetchData() {
70
+ try {
71
+ const res = await fetch(url);
72
+ const json = await res.json();
73
+ if (!cancelled) setData(json);
74
+ } catch (e) {
75
+ if (!cancelled) setError(e as Error);
76
+ } finally {
77
+ if (!cancelled) setLoading(false);
439
78
  }
440
79
  }
441
80
 
442
- document.addEventListener('mousedown', handleClickOutside);
443
- return () => document.removeEventListener('mousedown', handleClickOutside);
444
- }, [ref, handler]);
445
- }
446
- ```
447
-
448
- ### 4. State Management Patterns
449
-
450
- ```tsx
451
- // Context + useReducer for complex state
452
- interface AppState {
453
- user: User | null;
454
- theme: 'light' | 'dark';
455
- notifications: Notification[];
456
- }
457
-
458
- type AppAction =
459
- | { type: 'SET_USER'; payload: User | null }
460
- | { type: 'SET_THEME'; payload: 'light' | 'dark' }
461
- | { type: 'ADD_NOTIFICATION'; payload: Notification }
462
- | { type: 'REMOVE_NOTIFICATION'; payload: string };
463
-
464
- const initialState: AppState = {
465
- user: null,
466
- theme: 'light',
467
- notifications: [],
468
- };
469
-
470
- function appReducer(state: AppState, action: AppAction): AppState {
471
- switch (action.type) {
472
- case 'SET_USER':
473
- return { ...state, user: action.payload };
474
- case 'SET_THEME':
475
- return { ...state, theme: action.payload };
476
- case 'ADD_NOTIFICATION':
477
- return { ...state, notifications: [...state.notifications, action.payload] };
478
- case 'REMOVE_NOTIFICATION':
479
- return {
480
- ...state,
481
- notifications: state.notifications.filter(n => n.id !== action.payload),
482
- };
483
- default:
484
- return state;
485
- }
486
- }
487
-
488
- const AppContext = createContext<{
489
- state: AppState;
490
- dispatch: React.Dispatch<AppAction>;
491
- } | null>(null);
492
-
493
- export function AppProvider({ children }: { children: React.ReactNode }) {
494
- const [state, dispatch] = useReducer(appReducer, initialState);
495
-
496
- return (
497
- <AppContext.Provider value={{ state, dispatch }}>
498
- {children}
499
- </AppContext.Provider>
500
- );
501
- }
502
-
503
- export function useAppContext() {
504
- const context = useContext(AppContext);
505
- if (!context) {
506
- throw new Error('useAppContext must be used within AppProvider');
507
- }
508
- return context;
509
- }
510
-
511
- // Zustand for simpler state management (recommended for most cases)
512
- import { create } from 'zustand';
513
- import { persist } from 'zustand/middleware';
81
+ fetchData();
82
+ return () => { cancelled = true; };
83
+ }, [url]);
514
84
 
515
- interface UserStore {
516
- user: User | null;
517
- setUser: (user: User | null) => void;
518
- logout: () => void;
519
- }
520
-
521
- const useUserStore = create<UserStore>()(
522
- persist(
523
- (set) => ({
524
- user: null,
525
- setUser: (user) => set({ user }),
526
- logout: () => set({ user: null }),
527
- }),
528
- { name: 'user-storage' }
529
- )
530
- );
531
-
532
- // Usage
533
- function UserMenu() {
534
- const { user, logout } = useUserStore();
535
-
536
- if (!user) return <LoginButton />;
537
-
538
- return (
539
- <div>
540
- <span>Welcome, {user.name}</span>
541
- <button onClick={logout}>Logout</button>
542
- </div>
543
- );
85
+ return { data, loading, error };
544
86
  }
545
87
  ```
546
88
 
547
- ### 5. Form Handling
89
+ ### Form with Validation
548
90
 
549
91
  ```tsx
550
- // Controlled form with validation
551
92
  import { useForm } from 'react-hook-form';
552
93
  import { zodResolver } from '@hookform/resolvers/zod';
553
94
  import { z } from 'zod';
554
95
 
555
- const signUpSchema = z.object({
556
- name: z.string().min(2, 'Name must be at least 2 characters'),
557
- email: z.string().email('Invalid email address'),
558
- password: z.string()
559
- .min(8, 'Password must be at least 8 characters')
560
- .regex(/[A-Z]/, 'Password must contain an uppercase letter')
561
- .regex(/[0-9]/, 'Password must contain a number'),
562
- confirmPassword: z.string(),
563
- }).refine(data => data.password === data.confirmPassword, {
564
- message: "Passwords don't match",
565
- path: ['confirmPassword'],
96
+ const schema = z.object({
97
+ email: z.string().email(),
98
+ password: z.string().min(8),
566
99
  });
567
100
 
568
- type SignUpForm = z.infer<typeof signUpSchema>;
569
-
570
- function SignUpForm() {
571
- const {
572
- register,
573
- handleSubmit,
574
- formState: { errors, isSubmitting },
575
- reset,
576
- } = useForm<SignUpForm>({
577
- resolver: zodResolver(signUpSchema),
101
+ function LoginForm() {
102
+ const { register, handleSubmit, formState: { errors } } = useForm({
103
+ resolver: zodResolver(schema),
578
104
  });
579
105
 
580
- const onSubmit = async (data: SignUpForm) => {
581
- try {
582
- const response = await fetch('/api/signup', {
583
- method: 'POST',
584
- headers: { 'Content-Type': 'application/json' },
585
- body: JSON.stringify(data),
586
- });
587
-
588
- if (!response.ok) throw new Error('Signup failed');
589
-
590
- reset();
591
- // Handle success
592
- } catch (error) {
593
- // Handle error
594
- }
106
+ const onSubmit = (data: z.infer<typeof schema>) => {
107
+ console.log(data);
595
108
  };
596
109
 
597
110
  return (
598
- <form onSubmit={handleSubmit(onSubmit)} className="space-y-4">
599
- <div>
600
- <label htmlFor="name" className="block text-sm font-medium">
601
- Name
602
- </label>
603
- <input
604
- {...register('name')}
605
- id="name"
606
- type="text"
607
- className={`mt-1 block w-full rounded-md border ${
608
- errors.name ? 'border-red-500' : 'border-gray-300'
609
- }`}
610
- />
611
- {errors.name && (
612
- <p className="mt-1 text-sm text-red-500">{errors.name.message}</p>
613
- )}
614
- </div>
615
-
616
- <div>
617
- <label htmlFor="email" className="block text-sm font-medium">
618
- Email
619
- </label>
620
- <input
621
- {...register('email')}
622
- id="email"
623
- type="email"
624
- className={`mt-1 block w-full rounded-md border ${
625
- errors.email ? 'border-red-500' : 'border-gray-300'
626
- }`}
627
- />
628
- {errors.email && (
629
- <p className="mt-1 text-sm text-red-500">{errors.email.message}</p>
630
- )}
631
- </div>
632
-
633
- <div>
634
- <label htmlFor="password" className="block text-sm font-medium">
635
- Password
636
- </label>
637
- <input
638
- {...register('password')}
639
- id="password"
640
- type="password"
641
- className={`mt-1 block w-full rounded-md border ${
642
- errors.password ? 'border-red-500' : 'border-gray-300'
643
- }`}
644
- />
645
- {errors.password && (
646
- <p className="mt-1 text-sm text-red-500">{errors.password.message}</p>
647
- )}
648
- </div>
649
-
650
- <div>
651
- <label htmlFor="confirmPassword" className="block text-sm font-medium">
652
- Confirm Password
653
- </label>
654
- <input
655
- {...register('confirmPassword')}
656
- id="confirmPassword"
657
- type="password"
658
- className={`mt-1 block w-full rounded-md border ${
659
- errors.confirmPassword ? 'border-red-500' : 'border-gray-300'
660
- }`}
661
- />
662
- {errors.confirmPassword && (
663
- <p className="mt-1 text-sm text-red-500">{errors.confirmPassword.message}</p>
664
- )}
665
- </div>
666
-
667
- <button
668
- type="submit"
669
- disabled={isSubmitting}
670
- className="w-full py-2 px-4 bg-blue-600 text-white rounded-md hover:bg-blue-700 disabled:opacity-50"
671
- >
672
- {isSubmitting ? 'Signing up...' : 'Sign Up'}
673
- </button>
111
+ <form onSubmit={handleSubmit(onSubmit)}>
112
+ <input {...register('email')} />
113
+ {errors.email && <span>{errors.email.message}</span>}
114
+ <input {...register('password')} type="password" />
115
+ {errors.password && <span>{errors.password.message}</span>}
116
+ <button type="submit">Login</button>
674
117
  </form>
675
118
  );
676
119
  }
677
120
  ```
678
121
 
679
- ### 6. Performance Optimization
680
-
681
- ```tsx
682
- import { memo, useMemo, useCallback, lazy, Suspense } from 'react';
683
-
684
- // Memoized component to prevent unnecessary re-renders
685
- interface ExpensiveListItemProps {
686
- item: ListItem;
687
- onSelect: (id: string) => void;
688
- }
689
-
690
- const ExpensiveListItem = memo(function ExpensiveListItem({
691
- item,
692
- onSelect,
693
- }: ExpensiveListItemProps) {
694
- // Heavy render logic here
695
- return (
696
- <div onClick={() => onSelect(item.id)}>
697
- {item.name}
698
- </div>
699
- );
700
- });
701
-
702
- // Parent component with stable callbacks
703
- function ItemList({ items }: { items: ListItem[] }) {
704
- const [selectedId, setSelectedId] = useState<string | null>(null);
705
-
706
- // Stable callback reference
707
- const handleSelect = useCallback((id: string) => {
708
- setSelectedId(id);
709
- }, []);
710
-
711
- return (
712
- <div>
713
- {items.map(item => (
714
- <ExpensiveListItem
715
- key={item.id}
716
- item={item}
717
- onSelect={handleSelect}
718
- />
719
- ))}
720
- </div>
721
- );
722
- }
723
-
724
- // Lazy loading with code splitting
725
- const HeavyComponent = lazy(() => import('./HeavyComponent'));
726
- const AdminDashboard = lazy(() => import('./AdminDashboard'));
727
-
728
- function App() {
729
- const [showHeavy, setShowHeavy] = useState(false);
730
- const isAdmin = useUserStore(state => state.user?.role === 'admin');
731
-
732
- return (
733
- <div>
734
- <button onClick={() => setShowHeavy(true)}>Load Component</button>
735
-
736
- <Suspense fallback={<LoadingSpinner />}>
737
- {showHeavy && <HeavyComponent />}
738
- {isAdmin && <AdminDashboard />}
739
- </Suspense>
740
- </div>
741
- );
742
- }
743
-
744
- // Virtual list for large datasets
745
- import { useVirtualizer } from '@tanstack/react-virtual';
746
-
747
- function VirtualList({ items }: { items: Item[] }) {
748
- const parentRef = useRef<HTMLDivElement>(null);
749
-
750
- const virtualizer = useVirtualizer({
751
- count: items.length,
752
- getScrollElement: () => parentRef.current,
753
- estimateSize: () => 50,
754
- overscan: 5,
755
- });
756
-
757
- return (
758
- <div ref={parentRef} className="h-96 overflow-auto">
759
- <div
760
- style={{
761
- height: `${virtualizer.getTotalSize()}px`,
762
- position: 'relative',
763
- }}
764
- >
765
- {virtualizer.getVirtualItems().map(virtualItem => (
766
- <div
767
- key={virtualItem.key}
768
- style={{
769
- position: 'absolute',
770
- top: 0,
771
- left: 0,
772
- width: '100%',
773
- height: `${virtualItem.size}px`,
774
- transform: `translateY(${virtualItem.start}px)`,
775
- }}
776
- >
777
- {items[virtualItem.index].name}
778
- </div>
779
- ))}
780
- </div>
781
- </div>
782
- );
783
- }
784
- ```
785
-
786
- ### 7. Error Boundaries
787
-
788
- ```tsx
789
- import { Component, ErrorInfo, ReactNode } from 'react';
790
-
791
- interface ErrorBoundaryProps {
792
- children: ReactNode;
793
- fallback?: ReactNode;
794
- onError?: (error: Error, errorInfo: ErrorInfo) => void;
795
- }
796
-
797
- interface ErrorBoundaryState {
798
- hasError: boolean;
799
- error: Error | null;
800
- }
801
-
802
- class ErrorBoundary extends Component<ErrorBoundaryProps, ErrorBoundaryState> {
803
- constructor(props: ErrorBoundaryProps) {
804
- super(props);
805
- this.state = { hasError: false, error: null };
806
- }
122
+ ## Workflows
807
123
 
808
- static getDerivedStateFromError(error: Error): ErrorBoundaryState {
809
- return { hasError: true, error };
810
- }
124
+ ### Component Development
811
125
 
812
- componentDidCatch(error: Error, errorInfo: ErrorInfo) {
813
- console.error('Error caught by boundary:', error, errorInfo);
814
- this.props.onError?.(error, errorInfo);
815
- }
126
+ 1. Define props interface with TypeScript
127
+ 2. Create component with hooks
128
+ 3. Extract reusable logic to custom hooks
129
+ 4. Add error boundaries for fault isolation
130
+ 5. Write tests with Testing Library
816
131
 
817
- render() {
818
- if (this.state.hasError) {
819
- return this.props.fallback || (
820
- <div className="p-4 bg-red-50 border border-red-200 rounded-md">
821
- <h2 className="text-lg font-semibold text-red-800">Something went wrong</h2>
822
- <p className="text-sm text-red-600">{this.state.error?.message}</p>
823
- <button
824
- onClick={() => this.setState({ hasError: false, error: null })}
825
- className="mt-2 px-3 py-1 bg-red-600 text-white rounded text-sm"
826
- >
827
- Try again
828
- </button>
829
- </div>
830
- );
831
- }
832
-
833
- return this.props.children;
834
- }
835
- }
132
+ ### State Management Decision
836
133
 
837
- // Usage
838
- function App() {
839
- return (
840
- <ErrorBoundary
841
- fallback={<ErrorPage />}
842
- onError={(error) => logErrorToService(error)}
843
- >
844
- <MainContent />
845
- </ErrorBoundary>
846
- );
847
- }
848
134
  ```
849
-
850
- ### 8. Testing Patterns
851
-
852
- ```tsx
853
- import { render, screen, fireEvent, waitFor } from '@testing-library/react';
854
- import userEvent from '@testing-library/user-event';
855
- import { vi } from 'vitest';
856
-
857
- // Component test with user events
858
- describe('SignUpForm', () => {
859
- it('validates required fields', async () => {
860
- const user = userEvent.setup();
861
- render(<SignUpForm />);
862
-
863
- await user.click(screen.getByRole('button', { name: /sign up/i }));
864
-
865
- expect(await screen.findByText(/name must be at least 2 characters/i)).toBeInTheDocument();
866
- expect(screen.getByText(/invalid email address/i)).toBeInTheDocument();
867
- });
868
-
869
- it('submits form with valid data', async () => {
870
- const user = userEvent.setup();
871
- const mockSubmit = vi.fn();
872
-
873
- render(<SignUpForm onSubmit={mockSubmit} />);
874
-
875
- await user.type(screen.getByLabelText(/name/i), 'John Doe');
876
- await user.type(screen.getByLabelText(/email/i), 'john@example.com');
877
- await user.type(screen.getByLabelText(/^password$/i), 'Password123');
878
- await user.type(screen.getByLabelText(/confirm password/i), 'Password123');
879
-
880
- await user.click(screen.getByRole('button', { name: /sign up/i }));
881
-
882
- await waitFor(() => {
883
- expect(mockSubmit).toHaveBeenCalledWith({
884
- name: 'John Doe',
885
- email: 'john@example.com',
886
- password: 'Password123',
887
- confirmPassword: 'Password123',
888
- });
889
- });
890
- });
891
- });
892
-
893
- // Custom hook test
894
- import { renderHook, act } from '@testing-library/react';
895
-
896
- describe('useLocalStorage', () => {
897
- beforeEach(() => {
898
- localStorage.clear();
899
- });
900
-
901
- it('returns initial value when no stored value', () => {
902
- const { result } = renderHook(() => useLocalStorage('key', 'initial'));
903
- expect(result.current[0]).toBe('initial');
904
- });
905
-
906
- it('updates localStorage when value changes', () => {
907
- const { result } = renderHook(() => useLocalStorage('key', 'initial'));
908
-
909
- act(() => {
910
- result.current[1]('updated');
911
- });
912
-
913
- expect(result.current[0]).toBe('updated');
914
- expect(localStorage.getItem('key')).toBe('"updated"');
915
- });
916
- });
917
-
918
- // Component with async data test
919
- describe('UserProfile', () => {
920
- it('shows loading state then user data', async () => {
921
- const mockUser = { id: '1', name: 'John', email: 'john@example.com' };
922
-
923
- vi.spyOn(global, 'fetch').mockResolvedValueOnce({
924
- ok: true,
925
- json: async () => mockUser,
926
- } as Response);
927
-
928
- render(<UserProfile userId="1" />);
929
-
930
- expect(screen.getByText(/loading/i)).toBeInTheDocument();
931
-
932
- await waitFor(() => {
933
- expect(screen.getByText('John')).toBeInTheDocument();
934
- });
935
- });
936
-
937
- it('shows error state on fetch failure', async () => {
938
- vi.spyOn(global, 'fetch').mockRejectedValueOnce(new Error('Network error'));
939
-
940
- render(<UserProfile userId="1" />);
941
-
942
- await waitFor(() => {
943
- expect(screen.getByText(/error/i)).toBeInTheDocument();
944
- });
945
- });
946
- });
135
+ Local state only -> useState
136
+ Complex local state -> useReducer
137
+ Shared across tree -> Context + useReducer
138
+ App-wide state -> Zustand/Redux
139
+ Server state -> TanStack Query
947
140
  ```
948
141
 
949
- ## Use Cases
950
-
951
- ### Building a Dashboard
952
- ```tsx
953
- function Dashboard() {
954
- const { data: stats, loading } = useFetch<DashboardStats>('/api/stats');
955
- const { data: recentActivity } = useFetch<Activity[]>('/api/activity');
142
+ ## Best Practices
956
143
 
957
- if (loading) return <DashboardSkeleton />;
144
+ | Do | Avoid |
145
+ |----|-------|
146
+ | Use functional components | Class components |
147
+ | Extract custom hooks | Duplicating effect logic |
148
+ | Memoize expensive computations | Premature optimization |
149
+ | Handle loading/error states | Assuming success |
150
+ | Use keys for lists | Index as key for dynamic lists |
958
151
 
959
- return (
960
- <div className="grid grid-cols-1 md:grid-cols-3 gap-6">
961
- <StatsCard title="Total Users" value={stats?.users} />
962
- <StatsCard title="Revenue" value={`$${stats?.revenue}`} />
963
- <StatsCard title="Active Projects" value={stats?.projects} />
152
+ ## Project Structure
964
153
 
965
- <div className="col-span-full">
966
- <ActivityFeed activities={recentActivity || []} />
967
- </div>
968
- </div>
969
- );
970
- }
971
154
  ```
972
-
973
- ### Building a Todo App
974
- ```tsx
975
- function TodoApp() {
976
- const [todos, setTodos] = useLocalStorage<Todo[]>('todos', []);
977
- const [filter, setFilter] = useState<'all' | 'active' | 'completed'>('all');
978
-
979
- const filteredTodos = useMemo(() => {
980
- switch (filter) {
981
- case 'active': return todos.filter(t => !t.completed);
982
- case 'completed': return todos.filter(t => t.completed);
983
- default: return todos;
984
- }
985
- }, [todos, filter]);
986
-
987
- const addTodo = useCallback((text: string) => {
988
- setTodos(prev => [...prev, { id: crypto.randomUUID(), text, completed: false }]);
989
- }, [setTodos]);
990
-
991
- const toggleTodo = useCallback((id: string) => {
992
- setTodos(prev => prev.map(t =>
993
- t.id === id ? { ...t, completed: !t.completed } : t
994
- ));
995
- }, [setTodos]);
996
-
997
- return (
998
- <div>
999
- <TodoInput onAdd={addTodo} />
1000
- <FilterButtons filter={filter} onFilterChange={setFilter} />
1001
- <TodoList todos={filteredTodos} onToggle={toggleTodo} />
1002
- </div>
1003
- );
1004
- }
155
+ src/
156
+ ├── App.tsx
157
+ ├── main.tsx
158
+ ├── components/ # Reusable UI components
159
+ ├── hooks/ # Custom hooks
160
+ ├── pages/ # Route components
161
+ ├── stores/ # State management
162
+ ├── services/ # API calls
163
+ ├── utils/ # Helper functions
164
+ └── types/ # TypeScript types
1005
165
  ```
1006
166
 
1007
- ## Best Practices
1008
-
1009
- ### Do's
1010
- - Use functional components with hooks
1011
- - Keep components small and focused
1012
- - Extract reusable logic into custom hooks
1013
- - Use TypeScript for better type safety
1014
- - Memoize expensive computations
1015
- - Handle loading and error states
1016
- - Use proper key props for lists
1017
- - Clean up effects to prevent memory leaks
1018
-
1019
- ### Don'ts
1020
- - Don't use class components for new code
1021
- - Don't mutate state directly
1022
- - Don't overuse useEffect
1023
- - Don't forget dependency arrays
1024
- - Don't use index as key for dynamic lists
1025
- - Don't put business logic in components
1026
- - Don't skip error boundaries
1027
- - Don't ignore accessibility
1028
-
1029
- ## References
1030
-
1031
- - [React Documentation](https://react.dev)
1032
- - [React TypeScript Cheatsheet](https://react-typescript-cheatsheet.netlify.app)
1033
- - [Testing Library](https://testing-library.com/docs/react-testing-library/intro)
1034
- - [Zustand](https://zustand-demo.pmnd.rs)
1035
- - [React Hook Form](https://react-hook-form.com)
167
+ For detailed examples and patterns, see reference files above.