react-doctor-benchmarks

May 11, 2026 ยท View on GitHub

Reproducible react-doctor scores for popular open-source React frontends.

The scores below are produced by GitHub Actions on a weekly cron (and on demand). Every entry is scanned with npx react-doctor@latest --json --offline against a fresh clone of the upstream repo, and the resulting JSON is committed alongside this README so the leaderboard is fully auditable.

Leaderboard

RankProjectScoreErrorsWarningsFilesCommit
1executor๐ŸŸข โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–‘ 94/10031088de3f4c
2nodejs.org๐ŸŸข โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–‘โ–‘โ–‘ 86/1000196179125b760
3tldraw๐ŸŸก โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–‘โ–‘โ–‘โ–‘โ–‘โ–‘ 70/1007145768d2c9d8
4t3code๐ŸŸก โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–‘โ–‘โ–‘โ–‘โ–‘โ–‘ 68/10007652566ab8f93
5better-auth๐ŸŸก โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘ 64/1000628266e21d744
6excalidraw๐ŸŸก โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘ 63/1001555177b2b2815
7mastra๐ŸŸก โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘ 63/1002346820798c37f3
8payload๐ŸŸก โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘ 60/1004768424419a8e3
9typebot๐ŸŸก โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘ 57/100438220685eb843
10plane๐ŸŸก โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘ 56/100919448334c1bdd1
11medusajs/admin๐ŸŸก โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘ 56/10010591247456b06f
12rocket.chat๐ŸŸก โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘ 51/100572178969c7de992
13twenty๐Ÿ”ด โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘ 48/1008518331242c810199
14unkey๐Ÿ”ด โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘ 48/10027968381c262a0e
15shadcn/ui๐Ÿ”ด โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘ 46/10015234910148ca30ed
16trigger.dev๐Ÿ”ด โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘ 42/100371943593567e2a2
17formbricks๐Ÿ”ด โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘ 41/1001140568111d18b5c
18langfuse๐Ÿ”ด โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘ 36/100273176854c1af23a
19tooljet๐Ÿ”ด โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘ 33/10019059761459f33ff86
20onlook๐Ÿ”ด โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘ 32/100652034416a242be5
21cal.com๐Ÿ”ด โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘ 32/100431582425fb01494
22posthog๐Ÿ”ด โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘ 31/100682544018571be458f
23appsmith๐Ÿ”ด โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘ 31/10014524501314893572e
24supabase๐Ÿ”ด โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘ 30/100523305131595d1e8a
25sentry๐Ÿ”ด โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘ 30/1001793194165031da780
26lobehub/lobe-chat๐Ÿ”ด โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘ 29/1002946606164179ed4b5
27dub๐Ÿ”ด โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘ 25/10059336712133d450c5

Last updated 2026-05-11T07:33:40.103Z ยท react-doctor 0.1.6 ยท 27 scored, 0 skipped/failed ยท raw results in results/latest.json

How it works

  1. repos.yaml lists every benchmark target with its GitHub URL, the workspace project to scan, and any per-repo overrides (skip dead-code, skip install, etc.).
  2. The benchmark workflow fans out across the list using a matrix strategy. Each job clones one repo, attempts pnpm/npm/yarn/bun install --ignore-scripts (auto-detected from the lockfile), and runs npx -y react-doctor@latest <scanDir> --json --offline --fail-on none. If install fails, it falls back to --no-dead-code and scans the source anyway so a working score still lands.
  3. A final publish job downloads every per-repo artifact, writes results/latest.json, regenerates this README's leaderboard table, and commits the diff back to main using the default GITHUB_TOKEN. If nothing changed (idempotent rendering), the commit step is a no-op.

The harness pins nothing about the upstream repos by default โ€” every entry tracks HEAD of its default branch, and the SHA actually scanned is recorded in each result row so any score is reproducible. To pin a specific commit, set the ref field on a repos.yaml entry to a branch, tag, or SHA.

Consuming the leaderboard

Every CI run writes results/leaderboard.json โ€” a slim, stable JSON blob that downstream repos can fetch and drop in. It mirrors react-doctor's LeaderboardEntry interface so a one-shot replacement is straightforward.

Stable URL (always main, always the latest run):

https://raw.githubusercontent.com/millionco/react-doctor-benchmarks/main/results/leaderboard.json

Schema:

interface ConsumerLeaderboard {
  schemaVersion: 1;
  generatedAt: string;          // ISO 8601 UTC
  doctorVersion: string | null; // e.g. "0.0.47"
  source: { repo: string; path: string; docs: string };
  entries: Array<{
    slug: string;               // "tldraw"
    name: string;               // "tldraw"
    githubUrl: string;          // "https://github.com/tldraw/tldraw"
    packageName: string;        // workspace project name passed via --project
    score: number;              // 0โ€“100
    errorCount: number;
    warningCount: number;
    fileCount: number;          // affectedFileCount in react-doctor's JsonReport
    commitSha: string | null;   // SHA we actually scanned
    scannedAt: string;
  }>;                           // sorted desc by score
}

Example: regenerate react-doctor's leaderboard-entries.ts from CI:

# .github/workflows/refresh-leaderboard.yml in millionco/react-doctor
name: refresh leaderboard
on:
  schedule:
    - cron: "0 7 * * 1"  # one hour after react-doctor-benchmarks runs
  workflow_dispatch:

jobs:
  refresh:
    runs-on: ubuntu-latest
    permissions: { contents: write, pull-requests: write }
    steps:
      - uses: actions/checkout@v4
      - name: fetch latest leaderboard
        run: |
          curl -sSfL \
            https://raw.githubusercontent.com/millionco/react-doctor-benchmarks/main/results/leaderboard.json \
            -o /tmp/leaderboard.json
      - name: codegen leaderboard-entries.ts
        run: node scripts/codegen-leaderboard.mjs /tmp/leaderboard.json \
              > packages/website/src/app/leaderboard/leaderboard-entries.ts
      - uses: peter-evans/create-pull-request@v6
        with:
          title: "chore: refresh leaderboard from react-doctor-benchmarks"
          commit-message: "chore: refresh leaderboard"
          branch: chore/refresh-leaderboard

Where scripts/codegen-leaderboard.mjs is whatever projection makes sense for your downstream โ€” typically:

// scripts/codegen-leaderboard.mjs
import { readFileSync } from "node:fs";
const data = JSON.parse(readFileSync(process.argv[2], "utf8"));
const rows = data.entries.map((e) => `  ${JSON.stringify({
  name: e.name, githubUrl: e.githubUrl, packageName: e.packageName,
  score: e.score, errorCount: e.errorCount, warningCount: e.warningCount,
  fileCount: e.fileCount,
})},`).join("\n");
process.stdout.write(`// Auto-generated from ${data.source.repo} on ${data.generatedAt}\n` +
  `export const RAW_ENTRIES = [\n${rows}\n];\n`);

The blob is rewritten on every CI run, so even when scores don't change the generatedAt timestamp does โ€” you can safely diff or skip in your downstream codegen.

Adding a project

Open a PR that adds an entry to repos.yaml. The schema is defined and validated in scripts/lib/config.ts:

- slug: my-project          # kebab-case, must be unique
  name: my-project          # display name in the leaderboard
  githubUrl: https://github.com/owner/repo
  scanDir: apps/web         # optional, default "."
  project: "@my/web"        # optional, passes --project to react-doctor
  packageManager: pnpm      # optional, auto-detected from lockfile
  skipDeadCode: false       # optional, true โ†’ pass --no-dead-code
  skipInstall: false        # optional, true โ†’ don't install (implies skipDeadCode)

Once merged, the entry shows up the next time the workflow runs (weekly cron, or click Run workflow on the benchmark action).

Reproducing locally

pnpm install
pnpm tsx scripts/benchmark-repo.ts dub        # scan one entry
pnpm tsx scripts/aggregate.ts                  # collect results/per-repo/*.json โ†’ results/latest.json
pnpm tsx scripts/render-readme.ts              # splice into README between markers

Set REACT_DOCTOR_VERSION=1.2.3 to pin a specific upstream version (defaults to latest).

Layout

PathWhat
repos.yamlBenchmark targets (canonical source of truth).
results/latest.jsonAggregated leaderboard data; auto-generated.
results/per-repo/<slug>.jsonPer-repo result; auto-generated.
scripts/The harness (matrix prep, single-repo benchmark, aggregator, README renderer).
.github/workflows/benchmark.ymlThe automation.

Credits

Thirteen of the entries (tldraw, excalidraw, twenty, plane, formbricks, posthog, supabase, onlook, payload, sentry, cal.com, dub, nodejs.org) were originally compiled by hand for the react.doctor/leaderboard page in millionco/react-doctor. This repo turns that snapshot into a self-updating, auditable benchmark and adds ten more popular OSS React apps.

License

MIT.