# How React Doctor's Offline Mode Calculates Scores Without API Calls

> Learn how React Doctor calculates quality scores offline using local diagnostic rules and penalty calculations. Discover this powerful feature for faster, network-free analysis.

- Repository: [Million Software, Inc./react-doctor](https://github.com/millionco/react-doctor)
- Tags: internals
- Published: 2026-05-12

---

**React Doctor computes quality scores locally by analyzing diagnostic rules and applying penalty calculations, eliminating the need for network requests when the `--offline` flag is enabled.**

React Doctor is a CLI tool that audits React applications for performance anti-patterns and dead code. When running in environments without internet access or when explicitly configured for isolation, the tool switches to a deterministic local scoring algorithm that processes diagnostic data entirely on your machine.

## How the CLI Activates Offline Scoring

The offline workflow begins with option resolution in [`src/cli.ts`](https://github.com/millionco/react-doctor/blob/main/src/cli.ts). When you pass the `--offline` flag or when the tool detects a CI environment, the `resolveCliScanOptions` function (around line 176) merges CLI flags with user configuration to set `ResolvedScanOptions.offline` to **true**.

This boolean flows through the scan orchestration in [`src/scan.ts`](https://github.com/millionco/react-doctor/blob/main/src/scan.ts), where a conditional check at lines 817-819 selects the appropriate scoring path:

```typescript
const scoreResult = options.offline
  ? calculateScoreLocally(diagnostics)
  : await calculateScore(diagnostics);

```

When `options.offline` is **true**, the execution bypasses the async API call entirely and invokes the synchronous `calculateScoreLocally` function instead.

## The Local Scoring Algorithm

The pure-JavaScript implementation in [`src/utils/calculate-score-locally.ts`](https://github.com/millionco/react-doctor/blob/main/src/utils/calculate-score-locally.ts) converts diagnostic findings into a numeric score without external dependencies. The function aggregates linting results and dead-code analysis into a final rating through three distinct phases.

### Extracting Unique Rule Sets

First, the algorithm analyzes the collected `Diagnostic` objects using `collectUniqueRuleSets` to identify distinct error and warning categories. Rather than counting individual occurrences, it focuses on unique rule violations to determine the penalty scope.

### Applying the Penalty Formula

The scoring calculation (lines 49-53) uses constants defined in [`src/constants.ts`](https://github.com/millionco/react-doctor/blob/main/src/constants.ts):

- **PERFECT_SCORE**: 100 (baseline)
- **ERROR_RULE_PENALTY**: Deducted for each unique error rule
- **WARNING_RULE_PENALTY**: Deducted for each unique warning rule

The formula subtracts the total penalty from the perfect score and clamps the result to a minimum of 0:

```typescript
const score = Math.max(0, PERFECT_SCORE - 
  (uniqueErrors * ERROR_RULE_PENALTY) - 
  (uniqueWarnings * WARNING_RULE_PENALTY)
);

```

### Mapping Scores to Labels

The numeric result maps to human-readable quality grades:
- **Great**: High scores indicating minimal issues
- **Needs work**: Moderate scores suggesting improvements required  
- **Critical**: Low scores requiring immediate attention

## Fallback Behavior When APIs Fail

Even when running in standard mode, React Doctor includes defensive logic in [`src/utils/calculate-score.ts`](https://github.com/millionco/react-doctor/blob/main/src/utils/calculate-score.ts). The `calculateScore` function attempts remote evaluation via `tryScoreFromApi`, but automatically falls back to `calculateScoreLocally` if the network request fails or times out.

This ensures that **CI pipelines never fail due to network connectivity issues**, always receiving a deterministic score derived from local diagnostics.

## Implementation Examples

Trigger offline mode via command line:

```bash

# Scan without network requests, score calculated locally

npx react-doctor scan . --offline

```

Use offline scoring programmatically:

```typescript
import { scan } from "react-doctor";

// Force local calculation regardless of network status
await scan("./my-app", { offline: true });

```

Access the raw scoring utility directly:

```typescript
import { calculateScoreLocally } from "react-doctor/src/utils/calculate-score-locally";
import type { Diagnostic } from "react-doctor/src/types";

const diagnostics: Diagnostic[] = [
  // Populate from lint/dead-code output
];

const { score, label } = calculateScoreLocally(diagnostics);
console.log(`Score: ${score} – ${label}`);

```

When offline mode is active, the CLI displays the banner defined by `OFFLINE_MESSAGE` in [`src/constants.ts`](https://github.com/millionco/react-doctor/blob/main/src/constants.ts): *"Score calculated locally (offline mode)."*

## Summary

- **Offline activation** occurs through `--offline` flags or CI auto-detection in [`src/cli.ts`](https://github.com/millionco/react-doctor/blob/main/src/cli.ts), stored in `ResolvedScanOptions.offline`
- **Score calculation** happens in [`src/utils/calculate-score-locally.ts`](https://github.com/millionco/react-doctor/blob/main/src/utils/calculate-score-locally.ts) using penalty-based mathematics without HTTP requests
- **Algorithm details**: Subtracts `ERROR_RULE_PENALTY` and `WARNING_RULE_PENALTY` multipliers from `PERFECT_SCORE` (100), clamps to ≥0
- **Graceful degradation**: The remote scoring function in [`src/utils/calculate-score.ts`](https://github.com/millionco/react-doctor/blob/main/src/utils/calculate-score.ts) automatically falls back to local calculation when APIs are unreachable
- **Zero network traffic**: No calls to `https://www.react.doctor/api/score` occur when offline mode is enabled

## Frequently Asked Questions

### How does the penalty calculation work in offline mode?

The algorithm counts unique error and warning rules found in your diagnostics, then applies the formula: `score = 100 - (uniqueErrors × errorPenalty) - (uniqueWarnings × warningPenalty)`. The result is clamped to zero if penalties exceed 100, producing a score between 0 and 100.

### What happens if I run offline mode but the API is actually available?

When `options.offline` is **true**, the code in [`src/scan.ts`](https://github.com/millionco/react-doctor/blob/main/src/scan.ts) (lines 817-819) explicitly routes to `calculateScoreLocally`, completely skipping the `tryScoreFromApi` call. Even if the network is healthy, no HTTP requests are attempted, ensuring complete isolation as requested.

### Are offline scores comparable to API-generated scores?

Offline scores use the same `PERFECT_SCORE` baseline of 100 but may differ slightly from API-generated scores because the remote algorithm might weight rules differently or include additional heuristics. However, the local calculation provides a consistent, deterministic baseline for CI/CD pipelines and air-gapped environments.

### Can I customize the penalty constants for local scoring?

The penalty values (`ERROR_RULE_PENALTY`, `WARNING_RULE_PENALTY`) are defined as constants in [`src/constants.ts`](https://github.com/millionco/react-doctor/blob/main/src/constants.ts). Currently, these are hardcoded into the package build, but you can view these values to understand how rule severity impacts your final score calculation.