Cursor rule
example-structures/react-typescript/.cursor/rules/component-development.mdcModern React + TypeScript component patterns — hooks, typed props, performance optimization, forms, and testing
Cursor rules
Quality
58/100
Scores the file, not the repository.Length
1,320 words
7 headings · 5 code blocksRepository
18
— · pushed 0 days agoLast changed
2 days ago
First indexed 2 days ago.123456# React TypeScript Component Excellence78## Modern Component Architecture9- **Functional Components**: Use React hooks for state and lifecycle management10- **TypeScript Integration**: Comprehensive type safety with proper interface definitions11- **Performance Optimization**: Implement useMemo, useCallback, and React.memo strategically12- **Accessibility First**: Built-in ARIA support and keyboard navigation1314## Component Structure Best Practices15```typescript16import React, { useState, useCallback, useMemo, ReactNode } from 'react'17import { clsx } from 'clsx'1819// ✅ Define comprehensive prop interfaces20interface ButtonProps {21 children: ReactNode22 variant?: 'primary' | 'secondary' | 'outline' | 'ghost'23 size?: 'sm' | 'md' | 'lg'24 disabled?: boolean25 loading?: boolean26 onClick?: (event: React.MouseEvent<HTMLButtonElement>) => void27 type?: 'button' | 'submit' | 'reset'28 className?: string29 'data-testid'?: string30}3132// ✅ Modern functional component with comprehensive TypeScript33export const Button: React.FC<ButtonProps> = ({34 children,35 variant = 'primary',36 size = 'md',37 disabled = false,38 loading = false,39 onClick,40 type = 'button',41 className,42 'data-testid': testId,43 ...rest44}) => {45 // ✅ Memoized class computation for performance46 const buttonClasses = useMemo(() => clsx(47 'btn',48 `btn--${variant}`,49 `btn--${size}`,50 {51 'btn--disabled': disabled,52 'btn--loading': loading,53 },54 className55 ), [variant, size, disabled, loading, className])5657 // ✅ Optimized event handler58 const handleClick = useCallback((event: React.MouseEvent<HTMLButtonElement>) => {59 if (disabled || loading) {60 event.preventDefault()61 return62 }63 onClick?.(event)64 }, [disabled, loading, onClick])6566 return (67 <button68 type={type}69 className={buttonClasses}70 onClick={handleClick}71 disabled={disabled || loading}72 aria-disabled={disabled || loading}73 aria-busy={loading}74 data-testid={testId}75 {...rest}76 >77 {loading && (78 <span className="btn__spinner" aria-hidden="true">79 <LoadingSpinner size="sm" />80 </span>81 )}82 <span className={loading ? 'btn__content--loading' : 'btn__content'}>83 {children}84 </span>85 </button>86 )87}88```8990## Advanced Hook Patterns91```typescript92import { useState, useEffect, useCallback, useRef } from 'react'9394// ✅ Custom hook with comprehensive TypeScript95interface UseApiOptions<T> {96 immediate?: boolean97 onSuccess?: (data: T) => void98 onError?: (error: Error) => void99}100101interface UseApiReturn<T> {102 data: T | null103 loading: boolean104 error: Error | null105 execute: () => Promise<void>106 reset: () => void107}108109export function useApi<T>(110 apiCall: () => Promise<T>,111 options: UseApiOptions<T> = {}112): UseApiReturn<T> {113 const { immediate = false, onSuccess, onError } = options114115 const [data, setData] = useState<T | null>(null)116 const [loading, setLoading] = useState(false)117 const [error, setError] = useState<Error | null>(null)118 const cancelRef = useRef<AbortController | null>(null)119120 const execute = useCallback(async () => {121 // Cancel previous request122 if (cancelRef.current) {123 cancelRef.current.abort()124 }125126 const controller = new AbortController()127 cancelRef.current = controller128129 try {130 setLoading(true)131 setError(null)132133 const result = await apiCall()134135 if (!controller.signal.aborted) {136 setData(result)137 onSuccess?.(result)138 }139 } catch (err) {140 if (!controller.signal.aborted) {141 const error = err instanceof Error ? err : new Error('Unknown error')142 setError(error)143 onError?.(error)144 }145 } finally {146 if (!controller.signal.aborted) {147 setLoading(false)148 }149 }150 }, [apiCall, onSuccess, onError])151152 const reset = useCallback(() => {153 setData(null)154 setError(null)155 setLoading(false)156 if (cancelRef.current) {157 cancelRef.current.abort()158 }159 }, [])160161 useEffect(() => {162 if (immediate) {163 execute()164 }165166 return () => {167 if (cancelRef.current) {168 cancelRef.current.abort()169 }170 }171 }, [immediate, execute])172173 return { data, loading, error, execute, reset }174}175```176177## Form Handling Excellence178```typescript179import { useForm, Controller } from 'react-hook-form'180import { zodResolver } from '@hookform/resolvers/zod'181import { z } from 'zod'182183// ✅ Comprehensive form validation schema184const loginSchema = z.object({185 email: z186 .string()187 .min(1, 'Email is required')188 .email('Please enter a valid email address'),189 password: z190 .string()191 .min(8, 'Password must be at least 8 characters')192 .regex(/^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)/,193 'Password must contain at least one uppercase letter, one lowercase letter, and one number'),194 rememberMe: z.boolean().default(false),195})196197type LoginFormData = z.infer<typeof loginSchema>198199interface LoginFormProps {200 onSubmit: (data: LoginFormData) => Promise<void>201 loading?: boolean202}203204export const LoginForm: React.FC<LoginFormProps> = ({205 onSubmit,206 loading = false207}) => {208 const {209 control,210 handleSubmit,211 formState: { errors, isValid, isSubmitting },212 setError,213 reset,214 } = useForm<LoginFormData>({215 resolver: zodResolver(loginSchema),216 mode: 'onChange',217 defaultValues: {218 email: '',219 password: '',220 rememberMe: false,221 },222 })223224 const handleFormSubmit = useCallback(async (data: LoginFormData) => {225 try {226 await onSubmit(data)227 reset()228 } catch (error) {229 // Handle server validation errors230 if (error instanceof ValidationError) {231 error.fieldErrors.forEach(({ field, message }) => {232 setError(field as keyof LoginFormData, { message })233 })234 } else {235 setError('root', {236 message: 'An unexpected error occurred. Please try again.'237 })238 }239 }240 }, [onSubmit, reset, setError])241242 return (243 <form onSubmit={handleSubmit(handleFormSubmit)} className="login-form">244 <div className="form-group">245 <Controller246 name="email"247 control={control}248 render={({ field, fieldState }) => (249 <Input250 {...field}251 type="email"252 label="Email Address"253 placeholder="Enter your email"254 error={fieldState.error?.message}255 required256 data-testid="email-input"257 />258 )}259 />260 </div>261262 <div className="form-group">263 <Controller264 name="password"265 control={control}266 render={({ field, fieldState }) => (267 <Input268 {...field}269 type="password"270 label="Password"271 placeholder="Enter your password"272 error={fieldState.error?.message}273 required274 data-testid="password-input"275 />276 )}277 />278 </div>279280 <div className="form-group">281 <Controller282 name="rememberMe"283 control={control}284 render={({ field }) => (285 <Checkbox286 checked={field.value}287 onChange={field.onChange}288 label="Remember me"289 data-testid="remember-me-checkbox"290 />291 )}292 />293 </div>294295 {errors.root && (296 <div className="form-error" role="alert">297 {errors.root.message}298 </div>299 )}300301 <Button302 type="submit"303 variant="primary"304 size="lg"305 disabled={!isValid || isSubmitting || loading}306 loading={isSubmitting || loading}307 className="w-full"308 data-testid="submit-button"309 >310 {isSubmitting || loading ? 'Signing in...' : 'Sign In'}311 </Button>312 </form>313 )314}315```316317## State Management Patterns318```typescript319import { createContext, useContext, useReducer, ReactNode } from 'react'320321// ✅ Comprehensive state management with useReducer322interface User {323 id: string324 email: string325 name: string326 role: 'admin' | 'user' | 'guest'327}328329interface AuthState {330 user: User | null331 isAuthenticated: boolean332 loading: boolean333 error: string | null334}335336type AuthAction =337 | { type: 'AUTH_START' }338 | { type: 'AUTH_SUCCESS'; payload: User }339 | { type: 'AUTH_ERROR'; payload: string }340 | { type: 'LOGOUT' }341 | { type: 'CLEAR_ERROR' }342343const initialState: AuthState = {344 user: null,345 isAuthenticated: false,346 loading: false,347 error: null,348}349350function authReducer(state: AuthState, action: AuthAction): AuthState {351 switch (action.type) {352 case 'AUTH_START':353 return {354 ...state,355 loading: true,356 error: null,357 }358 case 'AUTH_SUCCESS':359 return {360 ...state,361 user: action.payload,362 isAuthenticated: true,363 loading: false,364 error: null,365 }366 case 'AUTH_ERROR':367 return {368 ...state,369 user: null,370 isAuthenticated: false,371 loading: false,372 error: action.payload,373 }374 case 'LOGOUT':375 return {376 ...state,377 user: null,378 isAuthenticated: false,379 loading: false,380 error: null,381 }382 case 'CLEAR_ERROR':383 return {384 ...state,385 error: null,386 }387 default:388 return state389 }390}391392interface AuthContextType extends AuthState {393 login: (email: string, password: string) => Promise<void>394 logout: () => void395 clearError: () => void396}397398const AuthContext = createContext<AuthContextType | undefined>(undefined)399400export const useAuth = (): AuthContextType => {401 const context = useContext(AuthContext)402 if (!context) {403 throw new Error('useAuth must be used within an AuthProvider')404 }405 return context406}407408interface AuthProviderProps {409 children: ReactNode410}411412export const AuthProvider: React.FC<AuthProviderProps> = ({ children }) => {413 const [state, dispatch] = useReducer(authReducer, initialState)414415 const login = useCallback(async (email: string, password: string) => {416 dispatch({ type: 'AUTH_START' })417418 try {419 const response = await authApi.login({ email, password })420 dispatch({ type: 'AUTH_SUCCESS', payload: response.user })421 } catch (error) {422 const message = error instanceof Error ? error.message : 'Login failed'423 dispatch({ type: 'AUTH_ERROR', payload: message })424 throw error425 }426 }, [])427428 const logout = useCallback(() => {429 authApi.logout()430 dispatch({ type: 'LOGOUT' })431 }, [])432433 const clearError = useCallback(() => {434 dispatch({ type: 'CLEAR_ERROR' })435 }, [])436437 const value = useMemo(() => ({438 ...state,439 login,440 logout,441 clearError,442 }), [state, login, logout, clearError])443444 return (445 <AuthContext.Provider value={value}>446 {children}447 </AuthContext.Provider>448 )449}450```451452## Testing Integration453```typescript454import { render, screen, fireEvent, waitFor } from '@testing-library/react'455import userEvent from '@testing-library/user-event'456import { vi } from 'vitest'457458// ✅ Comprehensive component testing459describe('LoginForm', () => {460 const mockOnSubmit = vi.fn()461462 beforeEach(() => {463 mockOnSubmit.mockClear()464 })465466 it('should render all form elements correctly', () => {467 render(<LoginForm onSubmit={mockOnSubmit} />)468469 expect(screen.getByTestId('email-input')).toBeInTheDocument()470 expect(screen.getByTestId('password-input')).toBeInTheDocument()471 expect(screen.getByTestId('remember-me-checkbox')).toBeInTheDocument()472 expect(screen.getByTestId('submit-button')).toBeInTheDocument()473 })474475 it('should validate email format', async () => {476 const user = userEvent.setup()477 render(<LoginForm onSubmit={mockOnSubmit} />)478479 const emailInput = screen.getByTestId('email-input')480 await user.type(emailInput, 'invalid-email')481 await user.tab() // Trigger blur event482483 await waitFor(() => {484 expect(screen.getByText('Please enter a valid email address')).toBeInTheDocument()485 })486 })487488 it('should submit form with valid data', async () => {489 const user = userEvent.setup()490 mockOnSubmit.mockResolvedValueOnce(undefined)491492 render(<LoginForm onSubmit={mockOnSubmit} />)493494 await user.type(screen.getByTestId('email-input'), 'test@example.com')495 await user.type(screen.getByTestId('password-input'), 'Password123')496 await user.click(screen.getByTestId('submit-button'))497498 await waitFor(() => {499 expect(mockOnSubmit).toHaveBeenCalledWith({500 email: 'test@example.com',501 password: 'Password123',502 rememberMe: false,503 })504 })505 })506507 it('should handle server errors gracefully', async () => {508 const user = userEvent.setup()509 mockOnSubmit.mockRejectedValueOnce(new Error('Invalid credentials'))510511 render(<LoginForm onSubmit={mockOnSubmit} />)512513 await user.type(screen.getByTestId('email-input'), 'test@example.com')514 await user.type(screen.getByTestId('password-input'), 'WrongPassword')515 await user.click(screen.getByTestId('submit-button'))516517 await waitFor(() => {518 expect(screen.getByText('An unexpected error occurred. Please try again.')).toBeInTheDocument()519 })520 })521})522```523
Also in tugkanboz/awesome-cursorrules
Diff this repo’s formatsOne repository carrying more than one format is the comparison this product exists for: does anyone actually write different content in each file, or is one a copy of the other?
| Repository | Format | Stack | Covers | Score | Changed |
|---|---|---|---|---|---|
| tugkanboz/awesome-cursorrulesexample-structures/cypress/.cursor/rules/api-testing.mdc · 18 | Cursor rules | testtesting-strategyapi | 58/100 | 2 days ago | |
| tugkanboz/awesome-cursorrulesexample-structures/cypress/.cursor/rules/testing-fundamentals.mdc · 18 | Cursor rules | testarchtesting-strategysecurity+2 | 62/100 | 2 days ago | |
| tugkanboz/awesome-cursorrulesexample-structures/next-js/.cursor/rules/app-router-patterns.mdc · 18 | Cursor rules | styleapido-not | 65/100 | 2 days ago | |
| tugkanboz/awesome-cursorrulesexample-structures/selenium-python/.cursor/rules/framework-architecture.mdc · 18 | Cursor rules | setuptestlint-formatstyle+3 | 93/100 | 2 days ago | |
| tugkanboz/awesome-cursorrulesexample-structures/selenium-python/.cursor/rules/page-object-patterns.mdc · 18 | Cursor rules | ui | 54/100 | 2 days ago | |
| tugkanboz/awesome-cursorrulesexample-structures/selenium-python/.cursor/rules/test-patterns.mdc · 18 | Cursor rules | teststylearchtesting-strategy+1 | 66/100 | 2 days ago | |
| tugkanboz/awesome-cursorrulesframeworks/cypress/.cursor/rules/cypress-excellence.mdc · 18 | Cursor rules | testtesting-strategysecurityperformance+1 | 58/100 | 2 days ago | |
| tugkanboz/awesome-cursorrulesrules/appium-mobile-test-automation-framework/.cursorrules · 18 | .cursorrules | teststylearchperformance+2 | 56/100 | 2 days ago | |
| tugkanboz/awesome-cursorrulesrules/cypress-javascript-test-automation-framework/.cursorrules · 18 | .cursorrules | testlint-formatstylearch+4 | 59/100 | 2 days ago | |
| tugkanboz/awesome-cursorrulesrules/k6-performance-test-framework/.cursorrules · 18 | .cursorrules | setupteststylearch+2 | 56/100 | 2 days ago | |
| tugkanboz/awesome-cursorrulesrules/playwright-javascript-test-automation-framework/.cursorrules · 18 | .cursorrules | testlint-formatstylearch+3 | 59/100 | 2 days ago | |
| tugkanboz/awesome-cursorrulesrules/restassured-java-framework/.cursorrules · 18 | .cursorrules | teststylearchsecurity+4 | 56/100 | 2 days ago | |
| tugkanboz/awesome-cursorrulesrules/selenium-net-test-automation-framework/.cursorrules · 18 | .cursorrules | teststylearchdeployment+1 | 56/100 | 2 days ago | |
| tugkanboz/awesome-cursorrulesrules/selenium-python-test-automation-framework/.cursorrules · 18 | .cursorrules | testlint-formatstylearch+3 | 59/100 | 2 days ago | |
| tugkanboz/awesome-cursorrulesrules/vitest-javascript-unit-test-framework/.cursorrules · 18 | .cursorrules | setupteststylearch+5 | 60/100 | 2 days ago | |
| tugkanboz/awesome-cursorrulesrules/webdriverio-javascript-test-automation-framework/.cursorrules · 18 | .cursorrules | testlint-formatstylearch+4 | 69/100 | 2 days ago |
Diff against example-structures/cypress/.cursor/rules/api-testing.mdc Diff against example-structures/cypress/.cursor/rules/testing-fundamentals.mdc Diff against example-structures/next-js/.cursor/rules/app-router-patterns.mdc Diff against example-structures/selenium-python/.cursor/rules/framework-architecture.mdc Diff against example-structures/selenium-python/.cursor/rules/page-object-patterns.mdc Diff against example-structures/selenium-python/.cursor/rules/test-patterns.mdc Diff against frameworks/cypress/.cursor/rules/cypress-excellence.mdc Diff against rules/appium-mobile-test-automation-framework/.cursorrules Diff against rules/cypress-javascript-test-automation-framework/.cursorrules Diff against rules/k6-performance-test-framework/.cursorrules Diff against rules/playwright-javascript-test-automation-framework/.cursorrules Diff against rules/restassured-java-framework/.cursorrules Diff against rules/selenium-net-test-automation-framework/.cursorrules Diff against rules/selenium-python-test-automation-framework/.cursorrules Diff against rules/vitest-javascript-unit-test-framework/.cursorrules Diff against rules/webdriverio-javascript-test-automation-framework/.cursorrules
