agent-rules-kit
Version:
CLI tool to bootstrap AI agent rules for multiple IDEs and frameworks. Generates optimized rules for Cursor, VS Code, Claude, and 6+ other AI coding assistants.
373 lines (295 loc) • 7.92 kB
Markdown
---
description: Best practices for React applications
globs: <root>/src/**/*.ts,<root>/src/**/*.tsx,<root>/src/**/*.js,<root>/src/**/*.jsx,<root>/components/**/*.ts,<root>/components/**/*.tsx,<root>/components/**/*.js,<root>/components/**/*.jsx
alwaysApply: false
---
# React Best Practices
This guide outlines the recommended best practices for React development in {projectPath}.
## Component Structure
### Functional Components
Use functional components with hooks as the default pattern:
```jsx
// ✅ Preferred: Functional component with hooks
const UserProfile = ({ userId }) => {
const [user, setUser] = useState(null);
const [loading, setLoading] = useState(true);
useEffect(() => {
fetchUser(userId).then((data) => {
setUser(data);
setLoading(false);
});
}, [userId]);
if (loading) return <Spinner />;
return <div>{user.name}</div>;
};
```
### Component Organization
Organize components by feature or page rather than by type:
```
src/
├── features/
│ ├── auth/
│ │ ├── components/
│ │ ├── hooks/
│ │ ├── utils/
│ │ └── index.js
│ ├── dashboard/
│ └── settings/
├── common/
│ ├── components/
│ ├── hooks/
│ └── utils/
```
## State Management
### Local State
Use local state for component-specific state:
```jsx
// Simple counter with local state
function Counter() {
const [count, setCount] = useState(0);
return (
<div>
<p>Count: {count}</p>
<button onClick={() => setCount(count + 1)}>Increment</button>
</div>
);
}
```
### Global State
For global state, choose the appropriate solution based on complexity:
- **Context API**: For simpler applications or shared state within a specific feature
- **Redux**: For complex applications with many state updates or when you need time-travel debugging
- **Zustand/Jotai/Recoil**: For simpler API with less boilerplate than Redux
```jsx
// Context API example
const ThemeContext = createContext();
function ThemeProvider({ children }) {
const [theme, setTheme] = useState('light');
return (
<ThemeContext.Provider value={{ theme, setTheme }}>
{children}
</ThemeContext.Provider>
);
}
// Hook to use the theme
function useTheme() {
return useContext(ThemeContext);
}
```
## Performance Optimization
### Memoization
Use `React.memo`, `useMemo`, and `useCallback` for performance optimization, but only when necessary:
```jsx
// Memoize expensive calculations
const MemoizedComponent = React.memo(({ value }) => {
// Only re-renders if value changes
return <div>{value}</div>;
});
function ParentComponent() {
const [count, setCount] = useState(0);
// Memoize callback to prevent unnecessary rerenders
const handleClick = useCallback(() => {
console.log('Clicked');
}, []);
// Memoize expensive calculations
const expensiveValue = useMemo(() => {
return computeExpensiveValue(count);
}, [count]);
return (
<div>
<button onClick={() => setCount(count + 1)}>Increment</button>
<MemoizedComponent value={expensiveValue} onClick={handleClick} />
</div>
);
}
```
### Virtual List
For long lists, use virtualization libraries like `react-window` or `react-virtualized`:
```jsx
import { FixedSizeList } from 'react-window';
const Row = ({ index, style }) => <div style={style}>Row {index}</div>;
const VirtualList = () => (
<FixedSizeList height={500} width='100%' itemCount={10000} itemSize={35}>
{Row}
</FixedSizeList>
);
```
## Code Splitting
Use dynamic imports and React.lazy for code splitting:
```jsx
import React, { Suspense, lazy } from 'react';
// Lazy load the component
const LazyComponent = lazy(() => import('./LazyComponent'));
function App() {
return (
<div>
<Suspense fallback={<div>Loading...</div>}>
<LazyComponent />
</Suspense>
</div>
);
}
```
## Custom Hooks
Extract reusable logic into custom hooks:
```jsx
// Custom hook for handling form state
function useForm(initialValues) {
const [values, setValues] = useState(initialValues);
const handleChange = useCallback((e) => {
const { name, value } = e.target;
setValues((prev) => ({ ...prev, [name]: value }));
}, []);
const resetForm = useCallback(() => {
setValues(initialValues);
}, [initialValues]);
return { values, handleChange, resetForm };
}
// Usage
function LoginForm() {
const { values, handleChange, resetForm } = useForm({
email: '',
password: '',
});
const handleSubmit = (e) => {
e.preventDefault();
// Process form submission
resetForm();
};
return (
<form onSubmit={handleSubmit}>
<input name='email' value={values.email} onChange={handleChange} />
<input
type='password'
name='password'
value={values.password}
onChange={handleChange}
/>
<button type='submit'>Login</button>
</form>
);
}
```
## API Calls
Use custom hooks for API calls:
```jsx
function useApi(url) {
const [data, setData] = useState(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
useEffect(() => {
let isMounted = true;
setLoading(true);
fetch(url)
.then((response) => response.json())
.then((data) => {
if (isMounted) {
setData(data);
setLoading(false);
}
})
.catch((error) => {
if (isMounted) {
setError(error);
setLoading(false);
}
});
return () => {
isMounted = false;
};
}, [url]);
return { data, loading, error };
}
// Usage
function UserList() {
const { data, loading, error } = useApi('/api/users');
if (loading) return <div>Loading...</div>;
if (error) return <div>Error: {error.message}</div>;
return (
<ul>
{data.map((user) => (
<li key={user.id}>{user.name}</li>
))}
</ul>
);
}
```
## Type Safety
Use TypeScript or PropTypes for type checking:
```tsx
// TypeScript example
interface UserProps {
name: string;
age: number;
email?: string; // Optional
}
const User: React.FC<UserProps> = ({ name, age, email }) => {
return (
<div>
<h2>{name}</h2>
<p>Age: {age}</p>
{email && <p>Email: {email}</p>}
</div>
);
};
```
## Accessibility
Follow accessibility best practices:
- Use semantic HTML elements
- Include proper labels for form elements
- Ensure keyboard navigation works
- Provide alternative text for images
- Test with screen readers
```jsx
// Accessible form element
<div>
<label htmlFor='email'>Email Address</label>
<input id='email' type='email' aria-describedby='email-help' />
<p id='email-help'>We'll never share your email.</p>
</div>
```
## Error Boundaries
Use error boundaries to catch JavaScript errors and display fallback UI:
```jsx
class ErrorBoundary extends React.Component {
constructor(props) {
super(props);
this.state = { hasError: false };
}
static getDerivedStateFromError(error) {
return { hasError: true };
}
componentDidCatch(error, errorInfo) {
// Log the error to an error reporting service
logErrorToService(error, errorInfo);
}
render() {
if (this.state.hasError) {
return <h1>Something went wrong.</h1>;
}
return this.props.children;
}
}
// Usage
<ErrorBoundary>
<MyComponent />
</ErrorBoundary>;
```
## Project Structure
Follow a consistent project structure:
```
src/
├── assets/ # Static files like images, fonts
├── components/ # Shared components
│ ├── common/ # Very generic components used across features
│ └── ui/ # UI components like buttons, inputs, etc.
├── features/ # Feature-based modules
├── hooks/ # Custom hooks
├── layouts/ # Layout components
├── pages/ # Page components
├── services/ # API services
├── store/ # State management
├── utils/ # Utility functions
├── App.jsx # Root component
└── index.jsx # Entry point
```