react-perf-analyzer
React performance + security scanner. Single binary. Zero config. SARIF output.
⚡ Powered by OXC — the fastest JS/TS parser in the ecosystem.
react-perf-analyzer is the orchestration layer for React quality:
oxc_linter → 400+ general JS/TS rules (opt-in via --external)
cargo-audit → CVE scanning for Rust deps (opt-in via --external)
OUR RULES → React-specific perf + security (always runs — zero config)
OUR REPORT → Unified HTML + SARIF + AI-ready prompts in one command
By default only the built-in React rules run. Pass
--externalto also invoke oxlint and cargo-audit. This keeps scans fast and avoids unexpected failures in environments where those tools are not installed.
Installation
Or download a pre-built binary from Releases.
Quick start
# Scan a project (text output)
# HTML report (auto-opens in browser)
# SARIF output for GitHub/GitLab inline PR annotations
# CI gate — fail only on high/critical issues
# Also run oxlint + cargo-audit (external tools, off by default)
# Pre-commit mode — only scan changed files (<10 ms)
# Suppress known issues with a baseline
# Custom TOML rules (no Rust required)
# AI prompt — generate fix prompts for Claude / Copilot / Cursor
# → Paste ai-fix-prompts/index.md into your AI assistant to start fixing
Rules
Performance (15 rules)
| Rule | What it detects |
|---|---|
no_inline_jsx_fn |
Inline arrow/function expressions in JSX props |
unstable_props |
Object/array literals in JSX props (new ref every render) |
large_component |
Components exceeding configurable line threshold |
no_new_context_value |
Object/array/function in Context.Provider value |
no_array_index_key |
Array index used as JSX key prop |
no_expensive_in_render |
.sort()/.filter()/.reduce() in JSX props without useMemo |
no_component_in_component |
Component definitions nested inside another component |
no_unstable_hook_deps |
Unstable objects/arrays in useEffect/useCallback deps array |
no_new_in_jsx_prop |
new expressions in JSX props |
no_use_state_lazy_init_missing |
useState(expensiveCall()) without lazy initializer |
no_json_in_render |
JSON.parse() / JSON.stringify() inside render |
no_object_entries_in_render |
Object.entries() / Object.keys() without useMemo |
no_regex_in_render |
RegExp literals created in render |
no_math_random_in_render |
Math.random() called on every render |
no_useless_memo |
useMemo around a primitive value |
Security (5 rules)
| Rule | Severity | What it detects |
|---|---|---|
no_unsafe_href |
Critical/Medium | javascript: URLs and dynamic href/src/action props |
no_xss_via_jsx_prop |
High | Unescaped req.query/req.body/req.params in JSX props |
no_hardcoded_secret_in_jsx |
High | High-entropy secrets in JSX props and variable declarations |
no_dangerously_set_inner_html_unescaped |
High | dangerouslySetInnerHTML without a safe sanitizer |
no_postmessage_wildcard |
Medium | postMessage(data, "*") without origin restriction |
AI Prompt Mode (--format ai-prompt)
Generate AI-ready fix prompts for every file with issues. Instead of auto-fixing (which is fragile for complex changes), the tool produces rich, self-contained markdown prompts that you paste into Claude, GitHub Copilot Chat, or Cursor to guide the AI through fixing each file.
Single file
# Writes: ai-fix-prompts.md — paste into your AI assistant
Directory mode (one prompt per file + orchestrator)
--outputis required for directory mode — you must specify a folder path ending with/. The tool will create the folder if it doesn't exist. Without--output, the result is written to a singleai-fix-prompts.mdfile.
# ✅ Correct — trailing slash tells the tool to use directory mode
# What gets created:
# ./ai-fix-prompts/index.md ← paste THIS into your AI assistant
# ./ai-fix-prompts/src_lib_Foo.tsx.md ← one file per component with issues
# ./ai-fix-prompts/src_lib_Bar.tsx.md
# ...
# ❌ Without --output → single file mode (all issues in one file)
# Writes: ai-fix-prompts.md (may be very large for big codebases)
Only index.md needs to be pasted into your AI. The individual .md files are read automatically by the AI as it works through each fix — their full paths are embedded in index.md so no manual file hunting is needed.
What a per-file prompt looks like
Each .md file in the output folder is a self-contained fix prompt for one source file. It contains:
- Every issue with line number, rule name, severity, and a plain-English explanation
- The full annotated source code (with
// ← ⚠ Issue Nmarkers on affected lines) - An instruction block telling the AI exactly how to fix all issues
Using the built-in test_fixtures/bad_component.tsx (15 issues) as an example:
The generated bad_component.tsx.md looks like this:
**Why it hurts**: Object/array literals create a new reference each render.
`React.memo` comparisons always fail, causing wasted re-renders.
**Problem**: Object literal in 'style' prop creates a new reference on every render.
Extract to a module-level constant or wrap with useMemo.
**Why it hurts**: A new function object is created on every render.
**Problem**: Inline arrow function in 'onClick' prop — extract to a named handler
or wrap with useCallback.
... (15 issues total) ...
You are an expert React developer. Fix ALL 15 issues listed above.
Do not change component logic, prop names, or render output.
Return the complete fixed file.
Each prompt is sized to fit comfortably within an AI context window (~500–36K tokens depending on file size).
Orchestrator workflow
The generated index.md is an agentic orchestrator prompt — paste the entire file into your AI assistant and it will:
- Ask 4 intake questions (scope, severity, PR, skip list) — reply with numbers like
1, 1, 2, 1 - Fix files one at a time in priority order (security → high-impact → by module)
- Auto-validate after each file: runs
react-perf-analyzer, TypeScript check, and ESLint — re-fixes any errors automatically - Update checkboxes in
index.mdas it goes ([ ]→[x] ✅ fixed 2026-04-15) - Ask before committing — choose: commit only / commit + PR / review diff first / skip
- Track progress — the same
index.mdacts as a live status board; re-paste it anytime to resume
# Example: scan a large monorepo
# Then paste ai-fix-prompts/index.md into Claude or Copilot Chat
# AI guides you through all 2,618 issues across 674 files — one file at a time
The file paths for both the prompt directory and source root are embedded in index.md, so the AI always knows exactly where to read and edit files.
Options
| Flag | Default | Description |
|---|---|---|
--format |
text |
Output: text | json | html | sarif | ai-prompt |
--output <PATH> |
stdout | Write output to file; append / for directory mode (ai-prompt only) |
--category |
all |
Rule category: all | perf | security |
--fail-on |
none |
Severity gate: none | low | medium | high | critical |
--external |
off | Also run oxlint (JS/TS rules) + cargo-audit (Rust CVEs) |
--only-changed |
off | Only analyze git-changed files (pre-commit mode) |
--baseline <FILE> |
— | Suppress known issues; fail only on new regressions |
--rules <FILE> |
auto | TOML file with custom lint rules (no Rust required) |
--max-component-lines |
300 |
Line threshold for large_component rule |
--include-tests |
off | Include *.test.*, *.spec.*, *.stories.* files |
CI Integration
Jenkins / Looper / Buildkite (shell-based CI)
For any CI system that runs shell steps directly, add two steps — download the
binary and run the scan. The tool exits 1 when issues meet --fail-on,
which automatically fails the PR check.
# Generic shell step — adapt syntax to your CI system
- name: download-react-perf-analyzer
sh: |
curl -sf -L -o /usr/local/bin/react-perf-analyzer \
https://github.com/rashvish18/react-perf-analyzer/releases/latest/download/react-perf-analyzer-linux-amd64
chmod +x /usr/local/bin/react-perf-analyzer
- name: react-perf-scan
sh: |
# Exit code 1 = issues found at/above --fail-on level → blocks PR merge
react-perf-analyzer ./src --fail-on high --format sarif --output results.sarif
A ready-to-use template is available at .looper.yml.example in this repo.
GitHub Actions
- name: React Perf + Security Scan
uses: rashvish18/react-perf-analyzer@v0.5
with:
path: './src'
fail-on: 'high'
upload-sarif: 'true' # Shows inline PR annotations
Or copy the bundled workflow template into your repo:
pre-commit hook
# Copy .pre-commit-config.yaml from this repo into your project
The hook runs --only-changed so only modified files are scanned on each commit.
GitLab CI
include:
- project: 'rashvish18/react-perf-analyzer'
file: 'templates/gitlab-ci-template.yml'
Baseline Mode
Suppress known issues so CI only fails on new regressions:
# 1. Generate baseline (commit this file)
# 2. Use in CI
Custom Rules (TOML DSL)
Define team-specific rules without writing Rust. Create react-perf-rules.toml:
[[]]
= "no-console-log"
= "Remove console.log() before merging"
= "medium"
= "perf"
= "console\\.log\\s*\\("
= "src/**/*.{ts,tsx}"
= "//\\s*nolint"
[[]]
= "no-inner-html"
= "Direct innerHTML causes XSS — use DOMPurify"
= "high"
= "security"
= "\\.innerHTML\\s*="
The file is auto-discovered in your project root. See react-perf-rules.toml.example for more examples.
HTML Report
The self-contained HTML report includes:
- 6 stat tiles — Total Issues, Files Scanned, Files with Issues, React Rules, oxlint, cargo-audit
(oxlint and cargo-audit tiles show
N/Awhen--externalwas not passed — distinguishes "not run" from "zero issues found") - Per-rule cards — click to filter the issue table
- Top 10 files bar chart — click bars to jump to file section
- Collapsible issue table — severity + source badge on every row
- Search — filter by filename in real time
# ✅ HTML report written to: react-perf-report.html (auto-opens on macOS)
Exit codes
| Code | Meaning |
|---|---|
0 |
No issues (or all below --fail-on threshold) |
1 |
Issues found at or above --fail-on threshold |
2 |
Fatal error (path not found, write error) |
Architecture
src/
├── main.rs # Entry point — parallel pipeline + orchestration
├── cli.rs # clap CLI flags
├── file_loader.rs # Recursive file discovery (walkdir)
├── parser.rs # OXC JS/TS/JSX parser wrapper
├── analyzer.rs # Runs built-in rules against parsed AST
├── orchestrator.rs # Runs oxlint + cargo-audit as subprocesses
├── baseline.rs # Baseline load/filter (suppress known issues)
├── changed_files.rs # Git-modified file detection (--only-changed)
├── custom_rules.rs # TOML rule DSL engine (regex line scanner)
├── reporter.rs # Text / JSON / HTML / SARIF / AI-prompt output
├── utils.rs # Byte offset → line/column
└── rules/
├── mod.rs # Rule trait, Issue, Severity, IssueSource
├── perf/ # 15 React performance rules
└── security/
└── react/ # 5 React security rules
Contributing
PRs welcome! See SAST_PLAN.md for the roadmap.