--- name: frontend-dev-guidelines description: Frontend development guidelines for React/TypeScript applications. Modern patterns including Suspense, lazy loading, useSuspenseQuery, file organization with features directory, styling best practices, routing, performance optimization, and TypeScript best practices. Use when creating components, pages, features, fetching data, styling, routing, or working with frontend code. --- # Frontend Development Guidelines ## Project Structure ``` src/ ├── features/ # Feature-based organization │ ├── auth/ │ │ ├── components/ │ │ ├── hooks/ │ │ ├── api/ │ │ └── index.ts │ ├── dashboard/ │ └── settings/ ├── components/ # Shared/common components │ ├── ui/ # Base UI components │ └── layout/ # Layout components ├── hooks/ # Shared hooks ├── utils/ # Utility functions ├── types/ # TypeScript types ├── api/ # API layer └── styles/ # Global styles ``` ## Component Patterns ### Functional Components with TypeScript ```tsx interface Props { title: string; onAction: (id: string) => void; isLoading?: boolean; } export function MyComponent({ title, onAction, isLoading = false }: Props) { // Component logic return (
{isLoading ? : }
); } ``` ### Suspense for Data Fetching ```tsx import { Suspense } from 'react'; function ParentComponent() { return ( }> ); } function DataComponent() { // useSuspenseQuery automatically suspends const { data } = useSuspenseQuery({ queryKey: ['items'], queryFn: fetchItems, }); return ; } ``` ### Lazy Loading Routes ```tsx import { lazy, Suspense } from 'react'; const Dashboard = lazy(() => import('./features/dashboard')); const Settings = lazy(() => import('./features/settings')); function App() { return ( }> } /> } /> ); } ``` ## State Management ### Local State ```tsx const [value, setValue] = useState(''); const [items, setItems] = useState([]); ``` ### Server State (TanStack Query) ```tsx // Queries const { data, isLoading, error } = useQuery({ queryKey: ['users', userId], queryFn: () => fetchUser(userId), }); // Mutations const mutation = useMutation({ mutationFn: updateUser, onSuccess: () => { queryClient.invalidateQueries({ queryKey: ['users'] }); }, }); ``` ### Global State (Zustand or Context) ```tsx // Simple global state with Zustand const useStore = create((set) => ({ theme: 'light', setTheme: (theme) => set({ theme }), })); ``` ## Styling Best Practices ### Tailwind CSS ```tsx
{title}
``` ### CSS Modules (when needed) ```tsx import styles from './Component.module.css';
{title}
``` ### Styled Components / Emotion (if used) ```tsx const Container = styled.div` display: flex; align-items: center; padding: ${({ theme }) => theme.spacing.md}; `; ``` ## Performance Optimization ### Memoization ```tsx // Memoize expensive computations const expensiveValue = useMemo(() => { return computeExpensiveValue(data); }, [data]); // Memoize callbacks const handleClick = useCallback((id: string) => { onAction(id); }, [onAction]); // Memoize components const MemoizedComponent = memo(function Component({ data }: Props) { return
{data.value}
; }); ``` ### Code Splitting ```tsx // Route-based splitting const Page = lazy(() => import('./Page')); // Component-based splitting const HeavyComponent = lazy(() => import('./HeavyComponent')); ``` ### Virtual Lists ```tsx import { useVirtualizer } from '@tanstack/react-virtual'; function VirtualList({ items }: { items: Item[] }) { const parentRef = useRef(null); const virtualizer = useVirtualizer({ count: items.length, getScrollElement: () => parentRef.current, estimateSize: () => 50, }); return (
{virtualizer.getVirtualItems().map((virtualItem) => (
{items[virtualItem.index].name}
))}
); } ``` ## TypeScript Best Practices ### Type Definitions ```tsx // Props interfaces interface ButtonProps { variant: 'primary' | 'secondary' | 'danger'; size?: 'sm' | 'md' | 'lg'; onClick: () => void; children: React.ReactNode; } // API response types interface ApiResponse { data: T; meta: { page: number; total: number; }; } // Utility types type PartialUser = Partial; type RequiredUser = Required; type UserKeys = keyof User; ``` ### Strict Null Checks ```tsx // Handle potentially null values const user = useUser(); if (!user) { return ; } // Now TypeScript knows user is defined return ; ``` ## Error Handling ### Error Boundaries ```tsx import { ErrorBoundary } from 'react-error-boundary'; function ErrorFallback({ error, resetErrorBoundary }: FallbackProps) { return (

Something went wrong:

{error.message}
); } ``` ## Testing ### Component Tests ```tsx import { render, screen, fireEvent } from '@testing-library/react'; describe('Button', () => { it('calls onClick when clicked', () => { const onClick = vi.fn(); render(); fireEvent.click(screen.getByRole('button')); expect(onClick).toHaveBeenCalledOnce(); }); }); ``` ### Hook Tests ```tsx import { renderHook, act } from '@testing-library/react'; describe('useCounter', () => { it('increments count', () => { const { result } = renderHook(() => useCounter()); act(() => { result.current.increment(); }); expect(result.current.count).toBe(1); }); }); ``` ## Resource Files For detailed patterns, see: - [component-patterns.md](resources/component-patterns.md) - [state-management.md](resources/state-management.md) - [performance.md](resources/performance.md) - [testing.md](resources/testing.md)