Skip to content
| Marketplace
Sign in
Visual Studio Code>Programming Languages>RenderGuardNew to Visual Studio Code? Get it now.
RenderGuard

RenderGuard

RenderGuard.dev

|
6 installs
| (0) | Free
Surface React performance patterns worth reviewing — inline objects, unstable deps, unmemoized state, and more. Signals, not mandates.
Installation
Launch VS Code Quick Open (Ctrl+P), paste the following command, and press enter.
Copied to clipboard
More Info

RenderGuard

Surface React performance patterns worth reviewing — right in your editor.

RenderGuard is a VS Code / Cursor extension that statically analyzes your React components and highlights patterns that can lead to performance issues. It's not a linter that demands you fix everything — it's a second pair of eyes that helps you make informed decisions about when memoization and optimization are actually worth the tradeoff.

Philosophy

Not every re-render is a problem. React is designed to re-render, and most re-renders are cheap — React compares the output and skips the DOM commit if nothing changed. Sprinkling useMemo and useCallback everywhere can actually add overhead rather than remove it.

RenderGuard doesn't tell you to fix everything it flags. Instead, it surfaces patterns that are worth a second look — places where performance could degrade at scale, in hot paths, or with expensive computations. You decide what's worth optimizing based on your app's real-world constraints.

When RenderGuard findings matter most:

  • Components that render large lists or complex trees
  • Hot paths that re-render on every keystroke or animation frame
  • Expensive computations (sorting, filtering, transforming large datasets) in the render body
  • Context providers with many consumers where value stability matters
  • Components wrapped in React.memo that receive unstable props (this is where inline objects/functions actually break memoization)

When you can safely ignore a finding:

  • Simple components with cheap render output
  • Inline handlers on leaf components that aren't wrapped in React.memo
  • Small derived computations on small datasets
  • Early-stage prototyping where readability matters more than optimization

What It Detects

RenderGuard ships with 9 pattern detectors across two tiers. Each detection is a signal, not a mandate.

Tier 1 — Per-Statement Analysis

Inline Objects & Arrays as Props

// Flagged: new reference every render — matters if Child uses React.memo
<Child style={{ color: 'red' }} />

// Consider when the child is memoized or the object is expensive to create
const style = useMemo(() => ({ color: 'red' }), []);
<Child style={style} />

Inline Functions as Props

// Flagged: new function reference every render
// Often harmless — only matters if the child is wrapped in React.memo
<Button onClick={() => handleClick(id)} />

// Worth it when Button is memoized and this prevents unnecessary child re-renders
const onClick = useCallback(() => handleClick(id), [id]);
<Button onClick={onClick} />

Missing React.memo

// Flagged: re-renders when parent re-renders, even if props haven't changed
// Often fine for simple components — React's diffing is fast
const UserCard = ({ name }) => <span>{name}</span>;

// Worth it for expensive components or those deep in a frequently-updating tree
const UserCard = React.memo(({ name }) => <span>{name}</span>);

Array Index as Key

// Flagged: causes unnecessary remounts when list order changes
items.map((item, index) => <Item key={index} />)

// Preferred: stable identity prevents remounting
items.map((item) => <Item key={item.id} />)

Missing or Unstable Hook Dependencies

// Flagged: missing deps defeats memoization entirely
const value = useMemo(() => compute(a, b));

// Flagged: object literal in deps re-triggers every render
const result = useMemo(() => x, [{ a: 1 }]);

// Correct
const value = useMemo(() => compute(a, b), [a, b]);

Broad Context Consumption

// Flagged: inline object in Provider causes all consumers to re-render
<AppContext.Provider value={{ user, theme }}>

// Flagged: subscribes to ALL context changes, even ones you don't use
const ctx = useContext(AppContext);

// Better: stable value + selective destructuring
const value = useMemo(() => ({ user, theme }), [user, theme]);
<AppContext.Provider value={value}>
const { theme } = useContext(AppContext);

Tier 2 — Data Flow Analysis

Unmemoized Derived State

// Flagged: recalculates on every render — costly with large datasets
const filtered = items.filter(i => i.active);
const sorted = [...data].sort((a, b) => a - b);

// Worth it when the dataset is large or the computation is expensive
const filtered = useMemo(() => items.filter(i => i.active), [items]);

Props Drilling

// Flagged: "theme" passes through without being used — consider Context
const Layout = ({ theme }) => <Sidebar theme={theme} />;

// Not flagged: "theme" is used in own logic
const Layout = ({ theme }) => <div className={theme}><Sidebar /></div>;

State Lifted Too High

// Flagged: state only used by one child — moving it down reduces parent re-renders
const Parent = () => {
  const [count, setCount] = useState(0);
  return <Counter count={count} setCount={setCount} />;
};

Features

  • Diagnostics — Inline hints for detected patterns, with hover details explaining when it matters
  • CodeLens — Pattern density score above each component for at-a-glance triage
  • Quick Fixes — One-click useMemo and useCallback wrapping when you decide it's warranted
  • Component Tree Sidebar — Activity bar panel showing all components, grouped by pattern density
  • Fully Configurable — Disable any detector you disagree with. Set severity to hint if you want gentle nudges instead of warnings
  • Fast — Static AST analysis via Babel, no type-checking overhead, debounced on keystrokes

Installation

From Marketplace

Search for "RenderGuard" in the VS Code / Cursor extensions panel, or visit the Marketplace listing.

From Source (Development)

git clone https://github.com/renderguard-dev/renderguard.git
cd renderguard/extension
npm install
npm run build

Then press F5 in VS Code / Cursor to launch the Extension Development Host.

Configuration

All settings live under renderguard.* in your VS Code / Cursor settings:

Setting Type Default Description
renderguard.enable boolean true Enable/disable all diagnostics
renderguard.severity string "warning" Severity level: error, warning, information, hint
renderguard.enableCodeLens boolean true Show pattern density scores above components
renderguard.patterns object all true Toggle individual detectors

Example: disable patterns you don't find useful

{
  "renderguard.patterns": {
    "inlineObjects": true,
    "inlineFunctions": false,
    "missingMemo": false,
    "unstableKeys": true,
    "unstableDeps": true,
    "broadContext": true,
    "derivedState": true,
    "propsDrilling": true,
    "liftedState": true
  }
}

Tip: If you find a detector too noisy for your codebase, set renderguard.severity to "hint" for unobtrusive inline annotations, or disable individual patterns entirely.

Architecture

renderguard/
├── extension/                  VS Code / Cursor extension
│   ├── src/
│   │   ├── extension.ts        Entry point — VS Code lifecycle
│   │   ├── analyzer.ts         Coordinates detectors, groups by component
│   │   ├── codelens.ts         CodeLens — pattern density above components
│   │   ├── codeactions.ts      Quick-fix Code Actions + Fix All
│   │   ├── treeview.ts         Component Tree sidebar panel
│   │   ├── types.ts            Shared interfaces
│   │   ├── utils/
│   │   │   ├── ast.ts          Babel parser wrapper (JSX + TypeScript)
│   │   │   └── diagnostics.ts  Severity mapping, scoring
│   │   └── patterns/
│   │       ├── inlineObjects.ts    Inline object/array literals as props
│   │       ├── inlineFunctions.ts  Inline arrow functions as props
│   │       ├── missingMemo.ts      Components missing React.memo
│   │       ├── unstableKeys.ts     Array index used as key
│   │       ├── unstableDeps.ts     Missing/unstable hook deps
│   │       ├── broadContext.ts     Inline Provider values, broad useContext
│   │       ├── derivedState.ts     Unmemoized .filter()/.sort()/etc.
│   │       ├── propsDrilling.ts    Props forwarded without own use
│   │       └── liftedState.ts      useState passed to single child
│   └── test/                   82 tests across 10 test files
└── website/                    Product website

Each pattern detector implements a simple interface:

interface PatternDetector {
  id: PatternId;
  detect(ast: File, document: TextDocument): RenderIssue[];
}

Adding a new detector is as simple as creating a file in extension/src/patterns/, implementing the interface, and registering it in extension/src/patterns/index.ts.

Roadmap

  • [x] Tier 1 detectors — Inline objects, inline functions, missing memo, unstable keys, unstable deps, broad context
  • [x] Tier 2 detectors — Derived state not memoized, props drilling, state lifted too high
  • [x] Component tree sidebar — Activity bar panel with pattern density per component
  • [ ] CI mode — Run as a CLI / ESLint plugin for pre-commit and CI pipeline checks
  • [ ] Configurable thresholds — Set minimum dataset size or component complexity for certain detectors
  • [ ] Render vs. commit awareness — Distinguish between cheap re-renders and costly DOM commits

Contributing

Contributions are welcome. Please open an issue first to discuss what you'd like to change.

License

MIT

  • Contact us
  • Jobs
  • Privacy
  • Manage cookies
  • Terms of use
  • Trademarks
© 2026 Microsoft