| name | react |
| description | [Applies to: **/*.{jsx,tsx}] Definitive guidelines for writing idiomatic, maintainable, and performant React applications using modern best practices and TypeScript. |
| source | cursor_mdc |
react Best Practices
This guide outlines the non-negotiable standards for building React applications within our team. Adherence ensures predictable behavior, simplifies debugging, and enables future optimizations.
1. Core React Principles: Purity & Rules of Hooks
Components and Hooks must be pure. They should always return the same output given the same inputs (props, state, context) and not cause side effects during rendering. Obey the Rules of Hooks without exception.
❌ BAD: Impure component / Side effect in render
function ProductList({ products }) {
products.sort((a, b) => a.name.localeCompare(b.name));
return ();
}
function MyComponent() {
if (Math.random() > 0.5) {
const [count, setCount] = useState(0);
}
return ();
}
✅ GOOD: Pure component / Correct Hook usage
import { useMemo, useState } from 'react';
function ProductList({ products }) {
const sortedProducts = useMemo(() =>
[...products].sort((a, b) => a.name.localeCompare(b.name)),
[products]
);
return ();
}
function MyComponent() {
const [count, setCount] = useState(0);
return ();
}
2. Code Organization & Naming
Organize code by feature using the bulletproof-react pattern. Use TypeScript (.tsx) for all components.
- One Component Per File: Except for small, pure, stateless components closely related to a parent.
- Naming:
- Components:
PascalCase (e.g., UserProfile.tsx)
- Custom Hooks:
use prefix + PascalCase (e.g., useAuth.ts)
- Functions/Variables:
camelCase
- CSS Classes:
kebab-case (via CSS Modules or utility classes)
✅ GOOD: Feature-based structure
src/
├── features/
│ ├── auth/
│ │ ├── components/
│ │ │ ├── LoginForm.tsx
│ │ │ └── AuthButton.tsx
│ │ ├── hooks/
│ │ │ └── useAuth.ts
│ │ └── api/auth.ts
│ └── products/
│ ├── components/
│ │ ├── ProductCard.tsx
│ │ └── ProductList.tsx
│ └── hooks/useProducts.ts
├── components/ui/ // Reusable, generic UI components
│ ├── Button.tsx
│ └── Modal.tsx
└── App.tsx
3. Component Design & Patterns
Prioritize function components with Hooks. Separate concerns into "smart" (data/logic) and "dumb" (presentational) components.
❌ BAD: Class components / Mixed concerns
class UserProfile extends React.Component { }
function ProductPage() {
const [products, setProducts] = useState([]);
useEffect(() => { }, []);
return ();
}
✅ GOOD: Function components / Separation of concerns
function UserProfile({ user }) { }
function ProductListContainer() {
const { products, isLoading } = useProducts();
if (isLoading) return <LoadingSpinner />;
return <ProductList products={products} />;
}
function ProductList({ products }) {
return (
<ul>
{products.map(product => <ProductCard key={product.id} product={product} />)}
</ul>
);
}
4. State Management
Start with local state (useState, useReducer). Lift state up when necessary. Use Context API for global state that rarely changes. For complex global state, use dedicated libraries (e.g., Zustand, Jotai, Redux Toolkit). Avoid prop drilling.
❌ BAD: Prop drilling
function Grandparent() {
const [theme, setTheme] = useState('dark');
return <Parent theme={theme} setTheme={setTheme} />;
}
function Parent({ theme, setTheme }) {
return <Child theme={theme} setTheme={setTheme} />;
}
function Child({ theme, setTheme }) {
return <Button onClick={() => setTheme('light')}>Toggle Theme</Button>;
}
✅ GOOD: Context API for global state
import { createContext, useContext, useState, ReactNode } from 'react';
type Theme = 'light' | 'dark';
type ThemeContextType = { theme: Theme; toggleTheme: () => void };
const ThemeContext = createContext<ThemeContextType | undefined>(undefined);
export function ThemeProvider({ children }: { children: ReactNode }) {
const [theme, setTheme] = useState<Theme>('dark');
const toggleTheme = () => setTheme(prev => (prev === 'dark' ? 'light' : 'dark'));
return (
<ThemeContext.Provider value={{ theme, toggleTheme }}>
{children}
</ThemeContext.Provider>
);
}
export const useTheme = () => {
const context = useContext(ThemeContext);
(context === ) {
();
}
context;
};
() {
{ theme, toggleTheme } = ();
;
}
5. Performance & Optimization
Optimize only when profiling indicates a bottleneck. Use React.memo, useCallback, useMemo judiciously.
❌ BAD: Premature optimization / Incorrect memoization
const MyButton = React.memo(({ onClick, children }) => <button onClick={onClick}>{children}</button>);
function Parent() {
const [count, setCount] = useState(0);
const handleClick = useCallback(() => {
console.log(count);
}, []);
return <Child onClick={handleClick} />;
}
✅ GOOD: Targeted optimization / Correct dependencies
import React, { useCallback, useMemo, useState } from 'react';
const ExpensiveList = React.memo(({ items }) => {
console.log('Rendering ExpensiveList');
return ();
});
function Parent() {
const [count, setCount] = useState(0);
const handleClick = useCallback(() => {
setCount(prev => prev + 1);
}, []);
const computedValue = useMemo(() => {
return count * 2;
}, [count]);
return (
<>
<ExpensiveList items={[{ id: 1, name: 'Item 1' }]} /> {/* Example usage */}
Count: {count}, Computed: {computedValue}
);
}
= .( {
.();
;
});
6. Common Pitfalls
- Never mutate props or state directly. Always create new objects/arrays.
- Never call component functions directly. Use JSX.
- Ensure
useEffect cleanup functions are always provided for subscriptions or timers.
- Correct
useEffect dependency arrays are critical to avoid infinite loops or stale closures.
❌ BAD: Direct mutation / Calling component as function
function MyComponent({ items }) {
items.push('new item');
const [data, setData] = useState({ value: 1 });
data.value = 2;
setData(data);
return MyOtherComponent();
}
✅ GOOD: Immutable updates / JSX usage
function MyComponent({ items }) {
const [data, setData] = useState({ value: 1 });
const updatedItems = [...items, 'new item'];
setData(prevData => ({ ...prevData, value: 2 }));
return <MyOtherComponent />;
}
7. Accessibility (A11y) & Testing
Build for accessibility from the start. Test components as a user would.
- Semantic HTML: Use native HTML elements (
<button>, <input>, <a>) whenever possible.
- ARIA Attributes: Use
aria-* attributes only when semantic HTML is insufficient.
- Keyboard Navigation: Ensure all interactive elements are keyboard accessible and have proper focus management.
- React Testing Library: Use
RTL to test component behavior, not implementation details.
❌ BAD: Non-semantic HTML / Untestable implementation
function MyButton() {
return <div onClick={() => alert('Clicked!')}>Click Me</div>;
}
test('MyComponent sets count to 1', () => {
const { instance } = render(<MyComponent />);
expect(instance.state.count).toBe(1);
});
✅ GOOD: Semantic HTML / User-centric testing
import { render, screen, fireEvent } from '@testing-library/react';
function MyButton() {
return <button type="button" onClick={() => alert('Clicked!')}>Click Me</button>;
}
test('MyButton alerts on click', () => {
render(<MyButton />);
fireEvent.click(screen.getByRole('button', { name: /click me/i }));
expect(window.alert).toHaveBeenCalledWith('Clicked!');
});