Static analysis for React performance. 9 pattern detectors, inline diagnostics, quick fixes, and a component tree sidebar — right in your editor.
const UserList = ({ users }) => { const sorted = users.sort((a, b) => a.name.localeCompare(b.name)); ⚠ Unmemoized derived state — wrap in useMemo return ( <div> {sorted.map((user, index) => ( ⚠ Array index as key <UserCard key={index} onClick={() => selectUser(user.id)} ⚠ Inline function — wrap in useCallback /> ))} </div> ); };
Existing tools only catch them at runtime. RenderGuard catches them at development time.
An inline object, a missing dependency array, an arrow function in JSX — one line is all it takes to trigger cascading re-renders.
Performance anti-patterns look like normal code. They slip through PRs because they're syntactically correct and functionally fine.
React DevTools Profiler and why-did-you-render require running the app, reproducing the issue, and tracing through component trees.
No browser extensions, no runtime overhead, no configuration required.
Squiggly underlines on every detected issue, just like a linter. Hover for details and suggested fixes.
Render risk scores displayed above each component — Low, Medium, or High — so you know where to focus.
One-click wrapping with useMemo and useCallback via Code Actions. Fix issues without leaving your flow.
Activity bar panel showing all components in the file, color-coded by risk, with expandable issue lists.
Enable or disable individual pattern detectors. Set severity to error, warning, info, or hint.
Static AST analysis via Babel — no type-checking overhead, no compilation step. Debounced on every keystroke.
Two tiers of analysis — from per-statement checks to data flow analysis across components.
// Flagged: new reference every render <Child style={{ color: 'red' }} /> // Safe const style = useMemo(() => ({ color: 'red' }), []); <Child style={style} />
// Flagged: new function every render <Button onClick={() => handleClick(id)} /> // Safe const onClick = useCallback(() => handleClick(id), [id]); <Button onClick={onClick} />
// Flagged: re-renders when parent does 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: recalculates every render 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