- Use controlled state at the right level
Keep state local when only one component needs it
Lift state only when necessary
// Bad: global store for a single toggle
const showModal = useGlobalStore(state => state.showModal);
// Good: local state
const [showModal, setShowModal] = useState(false);
- Memoize components and values a. React.memo for functional components
Prevents re-render if props haven’t changed:
const ListItem = React.memo(({ item }) =>
b. useMemo for expensive calculations
const filtered = useMemo(() => items.filter(i => i.active), [items]);
c. useCallback for stable callbacks
const handleClick = useCallback(() => doSomething(id), [id]);
- Use selectors in state management
Libraries like Redux / Zustand can trigger re-renders only for selected slices
const userName = useStore(state => state.user.name);
Avoid reading the entire state object → prevents unnecessary re-renders
- Split large components
Break into smaller components so only the relevant part re-renders
→ +
- Avoid inline objects/arrays in props
Inline objects/arrays are new references every render
// ❌
// ✅
- Use key properly in lists
React uses key to determine if elements changed
Stable keys prevent unnecessary re-mounts
{users.map(u => )}
- Avoid context overuse
Context updates re-render all consumers
Use state libraries (Zustand, Jotai) for frequently changing state
- Optimize re-renders from parent → child
Pass primitive props or memoized references
Avoid passing functions/objects inline unless memoized
- Use React Profiler for bottlenecks
Identify unnecessary re-renders
Optimize only hotspots
Measure actual impact before premature optimization
- Server-side considerations
For Next.js / SSR:
Use server components for static content
Only client components for interactive state
Top comments (0)