diff --git a/.cursorrules b/.cursor/rules/react-guidelines.mdc similarity index 82% rename from .cursorrules rename to .cursor/rules/react-guidelines.mdc index 1dbbbcb6..da0ea0ab 100644 --- a/.cursorrules +++ b/.cursor/rules/react-guidelines.mdc @@ -1,8 +1,13 @@ +--- +description: +globs: +alwaysApply: true +--- # Expert Guidelines You are an expert in TypeScript, Node.js, Next.js App Router, React, Shadcn UI, Radix UI and Tailwind. -Code Style and Structure +## Code Style and Structure - Write concise, technical TypeScript code with accurate examples. - Use functional and declarative programming patterns; avoid classes. @@ -12,43 +17,43 @@ Code Style and Structure - Use console.log({value}) instead of console.log(value) - Use onCallback instead of handleCallback -Naming Conventions +## Naming Conventions - Use lowercase with dashes for directories (e.g., components/auth-wizard). - Favor named exports for components. -TypeScript Usage +## TypeScript Usage - Use TypeScript for all code; prefer interfaces over types. - Avoid enums; use maps instead. - Use functional components with TypeScript interfaces. -Syntax and Formatting +## Syntax and Formatting - Use the "function" keyword for pure functions. - Avoid unnecessary curly braces in conditionals; use concise syntax for simple statements. - Use declarative JSX. -UI and Styling +## UI and Styling - Use Shadcn UI, Radix, and Tailwind for components and styling. - Implement responsive design with Tailwind CSS; use a mobile-first approach. -Performance Optimization +## Performance Optimization - Minimize 'use client', 'useEffect', and 'setState'; favor React Server Components (RSC). - Wrap client components in Suspense with fallback. - Use dynamic loading for non-critical components. - Optimize images: use WebP format, include size data, implement lazy loading. -Key Conventions +## Key Conventions - Optimize Web Vitals (LCP, CLS, FID). - Limit 'use client': - Favor server components and Next.js SSR. - Use only for Web API access in small components. - Avoid for data fetching or state management. +- Follow Next.js docs for Data Fetching, Rendering, and Routing. -Follow Next.js docs for Data Fetching, Rendering, and Routing. +Make sure not to start the dev server or run type of lint checking on agent mode. ---- diff --git a/.cursor/rules/react-patterns.mdc b/.cursor/rules/react-patterns.mdc new file mode 100644 index 00000000..fef759b5 --- /dev/null +++ b/.cursor/rules/react-patterns.mdc @@ -0,0 +1,168 @@ +--- +description: +globs: +alwaysApply: true +--- +# React Patterns and Best Practices + +## Core Philosophy + +- **UIs are thin wrappers over data** - avoid using local state (like useState) unless absolutely necessary and it's independent of business logic +- Even when local state seems needed, consider if you can flatten the UI state into a basic calculation +- useState is only necessary if it's truly reactive and cannot be derived + +## State Management + +- **Choose state machines over multiple useStates** - multiple useState calls make code harder to reason about +- Prefer a single state object with reducers for complex state logic +- Co-locate related state rather than spreading it across multiple useState calls + +## Component Architecture + +- **Create new component abstractions when nesting conditional logic** +- Move complex logic to new components rather than deeply nested conditionals +- Use ternaries only for small, easily readable logic +- Avoid top-level if/else statements in JSX - extract to components instead + +## Side Effects and Dependencies + +- **Avoid putting dependent logic in useEffects** - it causes misdirection about what the logic is doing +- Choose to explicitly define logic rather than depend on implicit reactive behavior +- When useEffect is necessary, be explicit about dependencies and cleanup +- Prefer derived state and event handlers over effect-driven logic + +## Timing and Async Patterns + +- **setTimeouts are flaky and usually a hack** - always provide a comment explaining why setTimeout is needed +- Consider alternatives like: + - Proper loading states + - Suspense boundaries + - Event-driven patterns + - State machines with delayed transitions + - requestAnimateFrame and queuMicrotask + +## Code Quality Impact + +These patterns prevent subtle bugs that pile up into major issues. While code may "work" without following these guidelines, violations often lead to: +- Hard-to-debug timing issues +- Unexpected re-renders +- State synchronization problems +- Complex refactoring requirements + +## Examples + +### ❌ Avoid: Multiple useState +```tsx +const [loading, setLoading] = useState(false); +const [error, setError] = useState(null); +const [data, setData] = useState(null); +``` + +### ✅ Prefer: State machine +```tsx +function useLazyRef(fn: () => T) { + const ref = React.useRef(null); + + if (ref.current === null) { + ref.current = fn(); + } + + return ref as React.RefObject; +} + +interface Store { + subscribe: (callback: () => void) => () => void + getState: () => T + setState: (key: K, value: T[K]) => void + notify: () => void +} + +function createStore( + listenersRef: React.RefObject void>>, + stateRef: React.RefObject, + onValueChange?: Partial<{ + [K in keyof T]: (value: T[K], store: Store) => void + }> +): Store { + const store: Store = { + subscribe: (cb) => { + listenersRef.current.add(cb); + return () => listenersRef.current.delete(cb); + }, + getState: () => stateRef.current, + setState: (key, value) => { + if (Object.is(stateRef.current[key], value)) return; + stateRef.current[key] = value; + onValueChange?.[key]?.(value, store); + store.notify(); + }, + notify: () => { + for (const cb of listenersRef.current) { + cb(); + } + }, + }; + + return store; +} + +function useStoreSelector( + store: Store, + selector: (state: T) => U +): U { + const getSnapshot = React.useCallback( + () => selector(store.snapshot()), + [store, selector] + ); + + return React.useSyncExternalStore( + store.subscribe, + getSnapshot, + getSnapshot + ); +} +``` + +### ❌ Avoid: Complex conditionals in JSX +```tsx +return ( +
+ {user ? ( + user.isAdmin ? ( + + ) : user.isPremium ? ( + + ) : ( + + ) + ) : ( + + )} +
+); +``` + +### ✅ Prefer: Component abstraction +```tsx +function UserDashboard({ user }) { + if (!user) return ; + if (user.isAdmin) return ; + if (user.isPremium) return ; + return ; +} +``` + +### ❌ Avoid: Effect-driven logic +```tsx +useEffect(() => { + if (user && user.preferences) { + setTheme(user.preferences.theme); + } +}, [user]); +``` + +### ✅ Prefer: Derived values +```tsx +const theme = user?.preferences?.theme ?? 'default'; +``` +