đź’» Coding
Senior React Developer Code Reviewer & Optimizer
Get expert-level code reviews for your React components. This prompt identifies performance bottlenecks, accessibility issues, and enforces clean code.
0Reviews
Prompt
Act as a Staff Frontend Engineer and React architecture expert. Review the following React code: [Insert React Code]. Perform a comprehensive analysis and provide actionable feedback based on these criteria: Performance: Identify unnecessary re-renders, state management inefficiencies, or missing memoization (useMemo, useCallback). Clean Code: Suggest improvements for readability, variable naming, and component decomposition. Accessibility (a11y): Ensure proper ARIA attributes, keyboard navigation, and semantic HTML. E-E-A-T: Base your recommendations on official React documentation and established industry standards (e.g., Airbnb style guide). Tone: Professional, calm, and confident. Be direct and constructive. Avoid fluff. Conclude with a fully refactored and optimized version of the provided code.
Instructions
Prepare your code: Paste the complete React component or hook you want reviewed. Include context: If the component relies on specific context or global state, mention it before pasting the code. Review feedback carefully: Read the explanations for performance and accessibility issues to learn from the review. Test the refactoring: Always test the provided refactored code in your local environment before pushing.
Examples
Example Input
Act as a Staff Frontend Engineer and React architecture expert. Review the following React code: [import React, { useState } from 'react';
function UserList({ users }) {
const [filter, setFilter] = useState('');
const filteredUsers = users.filter(user =>
user.name.toLowerCase().includes(filter.toLowerCase())
);
return (
<div>
<input
type="text"
onChange={(e) => setFilter(e.target.value)}
placeholder="Search users..."
/>
<ul>
{filteredUsers.map(user => (
<li key={user.id}>{user.name}</li>
))}
</ul>
</div>
);
}].
Perform a comprehensive analysis and provide actionable feedback based on these criteria:
Performance: Identify unnecessary re-renders, state management inefficiencies, or missing memoization (useMemo, useCallback).
Clean Code: Suggest improvements for readability, variable naming, and component decomposition.
Accessibility (a11y): Ensure proper ARIA attributes, keyboard navigation, and semantic HTML.
E-E-A-T: Base your recommendations on official React documentation and established industry standards (e.g., Airbnb style guide).
Tone: Professional, calm, and confident. Be direct and constructive. Avoid fluff.
Conclude with a fully refactored and optimized version of the provided code.Example Output
1) Re-computation on every render
Right now filteredUsers is recalculated on every render:
const filteredUsers = users.filter(...)
For small lists this is fine, but for larger lists (or frequent updates) this becomes measurable. Use useMemo so filtering only runs when users or filter changes (React docs recommend memoization for expensive derived computations).
Action:
Memoize normalizedFilter and filteredUsers with useMemo.
2) Uncontrolled input may cause subtle UX issues
The <input> is currently uncontrolled (no value prop). This isn’t a perf bug, but it can lead to inconsistencies if you later reset state or want to programmatically control the field.
Action:
Make the input controlled: value={filter}.
3) Lowercasing each user name on every keystroke
Each render lowercases every user.name. If the list is large, this adds overhead.
Action (optional, depending on scale):
Keep it simple (memoized filter is usually enough), or
Precompute a nameLower field when users are loaded (outside this component), or
Lowercase inside the filter but only after memoization (recommended baseline).
4) Handler allocations are fine, but you can stabilize if needed
The inline onChange creates a new function every render. In most apps this is negligible, but if this component is heavily optimized and passed down to memoized children, useCallback can help.
Action:
Use useCallback for onChange if you care about stable references.
Clean Code
1) Normalize early and name intent
filter is okay, but query / searchQuery is clearer. Also, normalize once:
searchQuery → raw input
normalizedQuery → trimmed + lowercased
2) Defensive handling for optional data
If users can be undefined or null, current code will throw.
Action:
Default users = [] in props, or guard.
3) Decompose when this grows
For now it’s small, but if you add avatars, selection, actions, etc., consider:
UserSearchInput
UserListItems
Don’t decompose prematurely, but keep the seams obvious.
Accessibility (a11y)
1) Input needs an accessible name
A placeholder is not a label. Screen readers need a label (<label> or aria-label). Prefer a visible <label>.
Action:
Add <label htmlFor="user-search">Search users</label>
Link with id="user-search".
2) Announce result count changes
When filtering, it helps to announce the number of results to screen readers via an aria-live region.
Action:
Add a visually-hidden status: role="status" aria-live="polite" with result count.
3) Semantic structure
A <ul> is fine. Consider showing an empty state message when no results match (also good for all users).
Action:
If filteredUsers.length === 0, render “No users found.”
4) Autocomplete hints
For a search field, autoComplete="off" or a relevant value can be chosen. Not mandatory, but sometimes improves UX.
E-E-A-T grounding (what these recommendations align with)
React docs: Derived data + memoization (useMemo) for expensive computations; stable callbacks (useCallback) when referential equality matters; controlled inputs for predictable state-driven UI.
Industry standards (Airbnb-ish conventions): Clear naming, avoid inline complex expressions in JSX, early returns for empty states, accessibility-first labeling.
Refactored + optimized code:
import React, { useCallback, useId, useMemo, useState } from 'react';
export function UserList({ users = [] }) {
const [searchQuery, setSearchQuery] = useState('');
const inputId = useId();
const normalizedQuery = useMemo(
() => searchQuery.trim().toLowerCase(),
[searchQuery]
);
const filteredUsers = useMemo(() => {
if (!normalizedQuery) return users;
return users.filter((user) =>
String(user?.name ?? '').toLowerCase().includes(normalizedQuery)
);
}, [users, normalizedQuery]);
const handleSearchChange = useCallback((e) => {
setSearchQuery(e.target.value);
}, []);
return (
<section aria-label="User list">
<div>
<label htmlFor={inputId}>Search users</label>
<input
id={inputId}
type="search"
value={searchQuery}
onChange={handleSearchChange}
placeholder="Search users…"
inputMode="search"
/>
{/* Screen-reader friendly updates */}
<div role="status" aria-live="polite" style={srOnly}>
{filteredUsers.length} user{filteredUsers.length === 1 ? '' : 's'} shown
</div>
</div>
{filteredUsers.length === 0 ? (
<p>No users found.</p>
) : (
<ul>
{filteredUsers.map((user) => (
<li key={user.id}>{user.name}</li>
))}
</ul>
)}
</section>
);
}
// Minimal “visually hidden” style without external CSS dependency
const srOnly = {
position: 'absolute',
width: 1,
height: 1,
padding: 0,
margin: -1,
overflow: 'hidden',
clip: 'rect(0, 0, 0, 0)',
whiteSpace: 'nowrap',
border: 0,
};