omgkit 2.1.0 → 2.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 (56) hide show
  1. package/package.json +1 -1
  2. package/plugin/skills/SKILL_STANDARDS.md +743 -0
  3. package/plugin/skills/databases/mongodb/SKILL.md +797 -28
  4. package/plugin/skills/databases/postgresql/SKILL.md +494 -18
  5. package/plugin/skills/databases/prisma/SKILL.md +776 -30
  6. package/plugin/skills/databases/redis/SKILL.md +885 -25
  7. package/plugin/skills/devops/aws/SKILL.md +686 -28
  8. package/plugin/skills/devops/docker/SKILL.md +466 -18
  9. package/plugin/skills/devops/github-actions/SKILL.md +684 -29
  10. package/plugin/skills/devops/kubernetes/SKILL.md +621 -24
  11. package/plugin/skills/frameworks/django/SKILL.md +920 -20
  12. package/plugin/skills/frameworks/express/SKILL.md +1361 -35
  13. package/plugin/skills/frameworks/fastapi/SKILL.md +1260 -33
  14. package/plugin/skills/frameworks/laravel/SKILL.md +1244 -31
  15. package/plugin/skills/frameworks/nestjs/SKILL.md +1005 -26
  16. package/plugin/skills/frameworks/nextjs/SKILL.md +407 -44
  17. package/plugin/skills/frameworks/rails/SKILL.md +594 -28
  18. package/plugin/skills/frameworks/react/SKILL.md +1006 -32
  19. package/plugin/skills/frameworks/spring/SKILL.md +528 -35
  20. package/plugin/skills/frameworks/vue/SKILL.md +1296 -27
  21. package/plugin/skills/frontend/accessibility/SKILL.md +1108 -34
  22. package/plugin/skills/frontend/frontend-design/SKILL.md +1304 -26
  23. package/plugin/skills/frontend/responsive/SKILL.md +847 -21
  24. package/plugin/skills/frontend/shadcn-ui/SKILL.md +976 -38
  25. package/plugin/skills/frontend/tailwindcss/SKILL.md +831 -35
  26. package/plugin/skills/frontend/threejs/SKILL.md +1298 -29
  27. package/plugin/skills/languages/javascript/SKILL.md +935 -31
  28. package/plugin/skills/languages/python/SKILL.md +489 -25
  29. package/plugin/skills/languages/typescript/SKILL.md +379 -30
  30. package/plugin/skills/methodology/brainstorming/SKILL.md +597 -23
  31. package/plugin/skills/methodology/defense-in-depth/SKILL.md +832 -34
  32. package/plugin/skills/methodology/dispatching-parallel-agents/SKILL.md +665 -31
  33. package/plugin/skills/methodology/executing-plans/SKILL.md +556 -24
  34. package/plugin/skills/methodology/finishing-development-branch/SKILL.md +595 -25
  35. package/plugin/skills/methodology/problem-solving/SKILL.md +429 -61
  36. package/plugin/skills/methodology/receiving-code-review/SKILL.md +536 -24
  37. package/plugin/skills/methodology/requesting-code-review/SKILL.md +632 -21
  38. package/plugin/skills/methodology/root-cause-tracing/SKILL.md +641 -30
  39. package/plugin/skills/methodology/sequential-thinking/SKILL.md +262 -3
  40. package/plugin/skills/methodology/systematic-debugging/SKILL.md +571 -32
  41. package/plugin/skills/methodology/test-driven-development/SKILL.md +779 -24
  42. package/plugin/skills/methodology/testing-anti-patterns/SKILL.md +691 -29
  43. package/plugin/skills/methodology/token-optimization/SKILL.md +598 -29
  44. package/plugin/skills/methodology/verification-before-completion/SKILL.md +543 -22
  45. package/plugin/skills/methodology/writing-plans/SKILL.md +590 -18
  46. package/plugin/skills/omega/omega-architecture/SKILL.md +838 -39
  47. package/plugin/skills/omega/omega-coding/SKILL.md +636 -39
  48. package/plugin/skills/omega/omega-sprint/SKILL.md +855 -48
  49. package/plugin/skills/omega/omega-testing/SKILL.md +940 -41
  50. package/plugin/skills/omega/omega-thinking/SKILL.md +703 -50
  51. package/plugin/skills/security/better-auth/SKILL.md +1065 -28
  52. package/plugin/skills/security/oauth/SKILL.md +968 -31
  53. package/plugin/skills/security/owasp/SKILL.md +894 -33
  54. package/plugin/skills/testing/playwright/SKILL.md +764 -38
  55. package/plugin/skills/testing/pytest/SKILL.md +873 -36
  56. package/plugin/skills/testing/vitest/SKILL.md +980 -35
@@ -1,61 +1,1035 @@
1
1
  ---
2
2
  name: react
3
- description: React development. Use for React components, hooks, state management.
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
4
14
  ---
5
15
 
6
- # React Skill
16
+ # React
7
17
 
8
- ## Patterns
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
9
35
 
10
- ### Functional Component
11
36
  ```tsx
37
+ // Well-typed component with props interface
12
38
  interface ButtonProps {
13
- onClick: () => void;
39
+ variant?: 'primary' | 'secondary' | 'danger';
40
+ size?: 'sm' | 'md' | 'lg';
41
+ disabled?: boolean;
42
+ loading?: boolean;
43
+ onClick?: () => void;
44
+ children: React.ReactNode;
45
+ }
46
+
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
+ };
68
+
69
+ 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
+ )}
86
+ </button>
87
+ );
88
+ }
89
+
90
+ // Card component with composition pattern
91
+ interface CardProps {
14
92
  children: React.ReactNode;
93
+ className?: string;
15
94
  }
16
95
 
17
- export function Button({ onClick, children }: ButtonProps) {
18
- return <button onClick={onClick}>{children}</button>;
96
+ interface CardHeaderProps {
97
+ title: string;
98
+ subtitle?: string;
99
+ action?: React.ReactNode;
19
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
+ };
20
133
  ```
21
134
 
22
- ### Hooks
135
+ ### 2. Essential Hooks Patterns
136
+
23
137
  ```tsx
24
- // useState
25
- const [count, setCount] = useState(0);
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]);
246
+
247
+ return (
248
+ <ul>
249
+ {results.map(result => (
250
+ <SearchResultItem key={result.id} result={result} />
251
+ ))}
252
+ </ul>
253
+ );
254
+ }
255
+
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]);
273
+
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]);
279
+
280
+ return (
281
+ <div>
282
+ <StatsBar stats={stats} />
283
+ <Table data={filteredData} />
284
+ </div>
285
+ );
286
+ }
287
+
288
+ // useRef for DOM access and mutable values
289
+ function AutoFocusInput() {
290
+ const inputRef = useRef<HTMLInputElement>(null);
291
+ const renderCount = useRef(0);
26
292
 
27
- // useEffect
28
- useEffect(() => {
29
- fetchData();
30
- return () => cleanup();
31
- }, [dependency]);
293
+ useEffect(() => {
294
+ // Focus input on mount
295
+ inputRef.current?.focus();
296
+ }, []);
32
297
 
33
- // useCallback
34
- const handleClick = useCallback(() => {
35
- doSomething(value);
36
- }, [value]);
298
+ useEffect(() => {
299
+ // Track renders without causing re-renders
300
+ renderCount.current += 1;
301
+ });
37
302
 
38
- // useMemo
39
- const computed = useMemo(() => expensiveCalc(data), [data]);
303
+ return (
304
+ <div>
305
+ <input ref={inputRef} placeholder="I'm auto-focused" />
306
+ <p>Render count: {renderCount.current}</p>
307
+ </div>
308
+ );
309
+ }
40
310
  ```
41
311
 
42
- ### Custom Hook
312
+ ### 3. Custom Hooks
313
+
43
314
  ```tsx
44
- function useUser(id: string) {
45
- const [user, setUser] = useState<User | null>(null);
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> {
324
+ const [data, setData] = useState<T | null>(null);
46
325
  const [loading, setLoading] = useState(true);
326
+ const [error, setError] = useState<Error | null>(null);
327
+
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]);
47
345
 
48
346
  useEffect(() => {
49
- fetchUser(id).then(setUser).finally(() => setLoading(false));
50
- }, [id]);
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
+ }, []);
51
418
 
52
- return { user, loading };
419
+ return size;
420
+ }
421
+
422
+ // Previous value hook
423
+ function usePrevious<T>(value: T): T | undefined {
424
+ const ref = useRef<T>();
425
+
426
+ useEffect(() => {
427
+ ref.current = value;
428
+ }, [value]);
429
+
430
+ return ref.current;
431
+ }
432
+
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();
439
+ }
440
+ }
441
+
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';
514
+
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
+ );
544
+ }
545
+ ```
546
+
547
+ ### 5. Form Handling
548
+
549
+ ```tsx
550
+ // Controlled form with validation
551
+ import { useForm } from 'react-hook-form';
552
+ import { zodResolver } from '@hookform/resolvers/zod';
553
+ import { z } from 'zod';
554
+
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'],
566
+ });
567
+
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),
578
+ });
579
+
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
+ }
595
+ };
596
+
597
+ 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>
674
+ </form>
675
+ );
676
+ }
677
+ ```
678
+
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
+ }
807
+
808
+ static getDerivedStateFromError(error: Error): ErrorBoundaryState {
809
+ return { hasError: true, error };
810
+ }
811
+
812
+ componentDidCatch(error: Error, errorInfo: ErrorInfo) {
813
+ console.error('Error caught by boundary:', error, errorInfo);
814
+ this.props.onError?.(error, errorInfo);
815
+ }
816
+
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
+ }
836
+
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
+ ```
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
+ });
947
+ ```
948
+
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');
956
+
957
+ if (loading) return <DashboardSkeleton />;
958
+
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} />
964
+
965
+ <div className="col-span-full">
966
+ <ActivityFeed activities={recentActivity || []} />
967
+ </div>
968
+ </div>
969
+ );
970
+ }
971
+ ```
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
+ );
53
1004
  }
54
1005
  ```
55
1006
 
56
1007
  ## Best Practices
57
- - Use functional components
58
- - Keep components small
59
- - Lift state up when needed
60
- - Use custom hooks for logic
61
- - Memoize expensive operations
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)