Not every re-render is a problem. RenderGuard highlights patterns that can cause performance issues — you decide what's worth optimizing. 9 detectors, inline hints, and quick fixes, right in your editor.
const UserList = ({ users }) => { const sorted = users.sort((a, b) => a.name.localeCompare(b.name)); ⚠ Unmemoized sort — consider useMemo for large datasets return ( <div> {sorted.map((user, index) => ( ⚠ Array index as key <UserCard key={index} onClick={() => selectUser(user.id)} ⚠ Inline function — matters if UserCard uses React.memo /> ))} </div> ); };
React is designed to re-render. Most re-renders are cheap. The hard part is knowing which ones actually matter.
React can call your component, diff the output, and skip the DOM commit entirely. A re-render doesn't always mean a costly update. The key is knowing when it does.
useMemo and useCallback aren't free. For cheap operations, the memoization overhead can exceed the cost of just re-computing. RenderGuard helps you find the patterns worth optimizing.
Performance patterns look like normal code. They're invisible in review because they're syntactically correct. You need static analysis to surface them for consideration.
RenderGuard surfaces patterns for review. You decide what to act on.
Subtle annotations on detected patterns. Hover for context on when it matters and when you can safely ignore it.
See which components have the most patterns worth reviewing — so you know where to focus when optimizing.
One-click useMemo/useCallback wrapping when you decide a pattern is worth optimizing. You choose, one at a time.
Activity bar panel showing all components in the file, grouped by pattern density, with expandable details.
Disagree with a detector? Disable it. Want gentle nudges? Set severity to "hint". Tailor it to your team's philosophy.
Static AST analysis via Babel — no type-checking overhead, no compilation step. Debounced on every keystroke.
Each detection is a signal, not a mandate. Two tiers of analysis — from per-statement checks to data flow across components.
// Flagged: new ref — matters if Child uses React.memo <Child style={{ color: 'red' }} /> // Safe const style = useMemo(() => ({ color: 'red' }), []); <Child style={style} />
// Flagged: new ref — often fine for leaf components <Button onClick={() => handleClick(id)} /> // Safe const onClick = useCallback(() => handleClick(id), [id]); <Button onClick={onClick} />
// Flagged: consider memo for expensive components const UserCard = (props) => { return <span>{props.name}</span>; }; // Safe const UserCard = React.memo((props) => { return <span>{props.name}</span>; });
// Flagged: causes remounts on reorder items.map((item, i) => <Item key={i} />) // Safe items.map((item) => <Item key={item.id} />)
// Flagged: missing deps defeats memoization const val = useMemo(() => compute(a, b)); // Safe const val = useMemo(() => compute(a, b), [a, b]);
// Flagged: subscribes to ALL changes const ctx = useContext(AppContext); // Safe: pick only what's needed const { theme } = useContext(AppContext);
// Flagged: costly with large datasets const filtered = items.filter(i => i.active); // Safe const filtered = useMemo( () => items.filter(i => i.active), [items] );
// Flagged: passes through without use const Layout = ({ theme }) => <Sidebar theme={theme} />; // Not flagged: used in own logic const Layout = ({ theme }) => <div className={theme}></div>;
// Flagged: state only used by one child const Parent = () => { const [count, setCount] = useState(0); return <Counter count={count} setCount={setCount} />; };
Works with VS Code and Cursor. Zero configuration needed.
Have a question, feature request, or found a bug? We'd love to hear from you.
Or email us directly at hello@renderguard.dev