# react-perf-analyzer
[](https://crates.io/crates/react-perf-analyzer)
[](https://crates.io/crates/react-perf-analyzer)
[](LICENSE)
[](https://github.com/rashvish18/react-perf-analyzer/actions/workflows/ci.yml)
**React performance + security scanner. Single binary. Zero config. SARIF output.**
> ⚡ Powered by [OXC](https://oxc-project.github.io/) — 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 across all sources in one command
```
> By default only the built-in React rules run. Pass `--external` to also invoke
> oxlint and cargo-audit. This keeps scans fast and avoids unexpected failures in
> environments where those tools are not installed.
---
## Installation
```bash
cargo install react-perf-analyzer
```
Or download a pre-built binary from [Releases](https://github.com/rashvish18/react-perf-analyzer/releases).
---
## Quick start
```bash
# Scan a project (text output)
react-perf-analyzer ./src
# HTML report (auto-opens in browser)
react-perf-analyzer ./src --format html
# SARIF output for GitHub/GitLab inline PR annotations
react-perf-analyzer ./src --format sarif --output results.sarif
# CI gate — fail only on high/critical issues
react-perf-analyzer ./src --fail-on high
# Also run oxlint + cargo-audit (external tools, off by default)
react-perf-analyzer ./src --external
# Pre-commit mode — only scan changed files (<10 ms)
react-perf-analyzer ./src --only-changed --fail-on high
# Suppress known issues with a baseline
react-perf-analyzer ./src --baseline .sast-baseline.json --fail-on high
# Custom TOML rules (no Rust required)
react-perf-analyzer ./src --rules react-perf-rules.toml
```
---
## Rules
### Performance (15 rules)
| `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)
| `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 |
---
## Options
| `--format` | `text` | Output: `text` \| `json` \| `html` \| `sarif` |
| `--output <FILE>` | stdout | Write output to file |
| `--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.
```yaml
# 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
```yaml
- 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 use the bundled workflow directly:
```yaml
# .github/workflows/scan.yml
jobs:
scan:
uses: rashvish18/react-perf-analyzer/.github/workflows/react-perf-analyzer.yml@main
```
### pre-commit hook
```bash
pip install pre-commit
# Copy .pre-commit-config.yaml from this repo into your project
pre-commit install
```
The hook runs `--only-changed` so only modified files are scanned on each commit.
### GitLab CI
```yaml
include:
- project: 'rashvish18/react-perf-analyzer'
file: '.github/workflows/gitlab-ci-template.yml'
```
---
## Baseline Mode
Suppress known issues so CI only fails on new regressions:
```bash
# 1. Generate baseline (commit this file)
react-perf-analyzer ./src --format json --output .sast-baseline.json
# 2. Use in CI
react-perf-analyzer ./src --baseline .sast-baseline.json --fail-on high
```
---
## Custom Rules (TOML DSL)
Define team-specific rules without writing Rust. Create `react-perf-rules.toml`:
```toml
[[rule]]
id = "no-console-log"
message = "Remove console.log() before merging"
severity = "medium"
category = "perf"
pattern = "console\\.log\\s*\\("
file_glob = "src/**/*.{ts,tsx}"
ignore_if = "//\\s*nolint"
[[rule]]
id = "no-inner-html"
message = "Direct innerHTML causes XSS — use DOMPurify"
severity = "high"
category = "security"
pattern = "\\.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/A` when `--external` was 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
```bash
react-perf-analyzer ./src --format html
# ✅ HTML report written to: react-perf-report.html (auto-opens on macOS)
```
---
## Exit codes
| `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 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
```bash
git clone https://github.com/rashvish18/react-perf-analyzer
cd react-perf-analyzer
cargo build
cargo test
cargo clippy -- -D warnings
```
PRs welcome! See `SAST_PLAN.md` for the roadmap.