LATEST SUCCESSFUL BENCHMARKCI run 37340871460 · 4ca9501 ↗

Compare compiled sources

Memoization in the incident triage app

The same 200-incident app, data and controls: No memoization, Manual memoization, and React Compiler. These are local Chromium lab measurements, not production field telemetry. The compiler arm uses the experimental Oxc integration; results describe this integration and workload, not every React Compiler implementation.

Overview

Manual memoization remains effective in this app: no demonstrated selection-speed winner between compiler and manual, while compiler ships 5.26% more minified JavaScript.

Metric No memoization Manual memoization React Compiler
Initial assets, gzip (kB) 78.15 78.28 (+0.14; +0.2%) 83.09 (+4.95; +6.3%)
Full table readiness, median (ms) 669.7 682.1 (+12.4; +1.9%) 691.2 (+21.5; +3.2%)
Open detail DOM, median (ms) 13.6 5.4 (-8.2; -60.3%) 6.0 (-7.6; -55.9%)
Narrow query AP DOM, median (ms) 35.8 29.5 (-6.3; -17.6%) 29.3 (-6.5; -18.2%)
Clear query DOM, median (ms) 62.2 55.6 (-6.6; -10.6%) 55.2 (-7.0; -11.3%)

Parentheses show absolute change and percentage against No memoization: negative means fewer bytes or less time, positive means more. Percentages use the unrounded report values, not rounded display cells. A zero reference gives n/a. Each row is its own endpoint; different workloads and protocols are never pooled into a score or ranking.

Manual optimization has lower bundle cost in this workload. Median selection-to-detail DOM at 4x CPU slowdown: 13.6 ms (No memoization); 5.4 ms (Manual memoization); 6 ms (React Compiler). These descriptive timings do not establish a repeatable speed winner. They do not establish equivalent responsiveness, identical memory behavior, or guaranteed gains in every scenario. Compare load, selection and query filtering separately; fewer render-work events do not by themselves demonstrate lower latency.

Six local load repetitions, three Lighthouse audits and twenty warmed trials per interaction in one Chromium process are advisory, not statistical proof or real-user evidence. Paint opportunity is not a guaranteed presentation timestamp. Render-work events use internal React 19.3.0 profiling fiber flags, not a public API; revalidate after upgrades. The Oxc compiler integration is experimental.

Page load

Initial-load production bytes

Normal production builds only. All sizes use decimal kB (1 kB = 1,000 bytes); exact bytes remain in comparison.json. Parentheses are differences against No memoization.

Metric No memoization Manual memoization React Compiler
JS raw (kB) 243.00 243.64 (+0.63; +0.3%) 256.45 (+13.45; +5.5%)
JS gzip (kB) 74.66 74.80 (+0.14; +0.2%) 79.61 (+4.95; +6.6%)
CSS raw (kB) 12.72 12.72 (0.00; 0.0%) 12.72 (0.00; 0.0%)
CSS gzip (kB) 3.49 3.49 (0.00; 0.0%) 3.49 (0.00; 0.0%)
TOTAL raw (kB) 255.72 256.35 (+0.63; +0.2%) 269.17 (+13.45; +5.3%)
TOTAL gzip (kB) 78.15 78.28 (+0.14; +0.2%) 83.09 (+4.95; +6.3%)

Manifest entry JS, static imports, and attached CSS counted once per file. Source maps, dynamic imports, and profile builds excluded. Bundle bytes do not measure parse time or interaction latency. Separately loaded chunks: No memoization: none; Manual memoization: none; React Compiler: none.

User-perceived load time (UPLT)

UPLT is navigation start to the full 200-row incident table completing two animation frames (a paint opportunity). Fresh isolated contexts, normal production builds, 4x CDP CPU slowdown, six runs covering all six app-order permutations, with each app in each position twice; no network throttling. This is a lab readiness proxy, not a Core Web Vital, guaranteed paint or an input-response metric.

Metric No memoization Manual memoization React Compiler
Median readiness (ms) 669.7 682.1 (+12.4; +1.9%) 691.2 (+21.5; +3.2%)
Run 1 / 2 / 3 / 4 / 5 / 6 (ms) 686.9 / 704.1 / 668.9 / 668.5 / 670.5 / 646.2 656.2 / 694.9 / 701.5 / 669.3 / 664.2 / 711.7 693.3 / 689 / 653.2 / 697.1 / 669.1 / 726.6

Load memory (normal production builds)

Separate memory-only loads: six repetitions per app, all six app orders, a fresh Chromium browser and context for each load, cache disabled, 1440 x 900 viewport and 4x CPU slowdown. Readiness is the full 200-row table followed by two animation-frame callbacks. No React profiling build, DevTools hook, CPU sampling, or forced GC runs during the load window; memory polling is not added to the existing load/interaction latency runs.

JS heap used is CDP Runtime.getHeapUsage.usedSize (V8 used heap). Embedder heap used is embedderHeapUsedSize (the embedder's garbage-collected heap, including Blink-managed objects); it is not all native/DOM memory or total tab/process memory. CDP reports the corresponding isolate, not just React or a single execution context. Allocated JS heap (totalSize) and array-buffer/external-string backing storage (backingStorageSize) are retained separately in the raw data, not added into a claimed memory total.

Metric Observation No memoization MiB, median [min, max] Manual memoization MiB, median [min, max] React Compiler MiB, median [min, max] Manual vs no memoization Compiler vs no memoization Compiler vs manual
JS heap used Ready, before forced GC 3.229 [3.218, 3.236] 3.290 [3.280, 3.313] 3.686 [3.673, 3.691] +1.91% +14.16% +12.02%
JS heap used Sampled load-window peak 3.229 [3.218, 3.236] 3.290 [3.280, 3.313] 3.686 [3.673, 3.691] +1.91% +14.16% +12.02%
JS heap used Ready, after forced GC 2.590 [2.590, 2.590] 2.641 [2.640, 2.641] 3.019 [3.019, 3.019] +1.94% +16.55% +14.33%
Embedder heap used Ready, before forced GC 13.656 [13.656, 13.656] 13.656 [13.656, 13.656] 13.656 [13.656, 13.656] 0.00% 0.00% 0.00%
Embedder heap used Sampled load-window peak 13.656 [13.656, 13.656] 13.656 [13.656, 13.656] 13.656 [13.656, 13.656] 0.00% 0.00% 0.00%
Embedder heap used Ready, after forced GC 7.200 [7.200, 7.200] 7.200 [7.199, 7.200] 7.200 [7.199, 7.200] 0.00% 0.00% 0.00%

Main percentages compare unrounded medians against No memoization; the final compiler-versus-manual column uses manual as its reference. Negative means less memory, positive means more. Min/max show the six runs, not confidence intervals. No memory winner or statistical significance is inferred from this small local sample.

Samples use Runtime.getHeapUsage with a requested 20 ms pause after each response. CDP scheduling and main-thread work can delay samples; timestamps bracket each request in the raw data. The sampled peak is the largest observed counter from navigation start through the readiness snapshot, not the true allocation peak; independent counter maxima need not occur together. Polling adds overhead, and natural GC can occur before any sample. Ready/pre-forced-GC values therefore include GC-timing noise, not just live retained objects.

Observed coverage: 3-3 load-window samples per run, plus the readiness snapshot. The longest load-window/readiness request spanned 387.4 ms; the largest readiness-to-snapshot completion lag was 5.5 ms. Sparse samples cannot rule out larger transient allocations between observations.

The post-GC snapshot follows HeapProfiler.collectGarbage and is a separate retained-memory diagnostic, not a cold-load peak or a leak test. The blank-page snapshot is preserved for context but is not subtracted: navigation can change the isolate. These are cold browser/cache runs, not cold OS/disk caches or real-user device measurements. Raw load-memory samples include all counters, request timestamps, run order and readiness observations. Differences are observations for this workload, not proof of a compiler-caused reduction.

Lighthouse (mobile lab)

Lighthouse 12.8.2, mobile preset with simulate throttling, normal builds. Three fresh Chrome launches per app, alternating order. Scores are 0-100, timings are milliseconds and CLS is unitless. Median metrics are advisory; this protocol is separate from the desktop 4x CDP setup. No memoization raw audit / Manual memoization raw audit / React Compiler raw audit.

Metric No memoization Manual memoization React Compiler
Performance score 100.0 100.0 100.0
Accessibility score 95.0 95.0 95.0
FCP (ms) 1355.0 1356.6 1356.2
LCP (ms) 1355.0 1356.6 1356.2
TBT (ms) 80.0 78.0 86.0
Speed Index (ms) 1355.0 1356.6 1356.2
CLS 0.000 0.000 0.000
Time to Interactive (ms) 1485.0 1482.9 1489.8

Row selection

Path: Platform queue, 67 visible incidents, no detail selected, open INC-0001. Closing the detail is an untimed reset, not a measured endpoint.

Selection responsiveness (normal production builds)

One open/close warm-up followed by 20 warmed trials per app, isolated contexts, rotated app order and 4x CDP CPU slowdown. Timing starts in the browser click handler and stops at the correct detail DOM mutation, then at two animation-frame callbacks. The latter is a paint opportunity, not guaranteed presentation or INP. Playwright latency and React profiling overhead are excluded. Per-trial results remain in selection-latency.json.

Metric No memoization Manual memoization React Compiler
DOM median (ms) 13.6 5.4 6.0
DOM p90 (ms) 18.2 7.6 8.8
Two-frame opportunity median (ms) 33.7 24.2 26.5
Two-frame opportunity p90 (ms) 42.7 32.8 29.7

Selection component work (profiling builds)

Same selection state and target, separate native-speed profiling builds, one warm-up and medians of three rotated repetitions. Render-work events count completed update work, not milliseconds or DOM mutations. Counts and timings come from different runs and are not directly interchangeable.

Metric No memoization Manual memoization React Compiler
Whole-app commits 1 1 1
Queue render-work events 0 0 0
Row render-work events 67 1 67
Open-button render-work events 67 0 0
Favorite-button render-work events 67 0 0
Row mounts (not update events) 0 0 0
Open-button mounts 0 0 0
Favorite-button mounts 0 0 0

Selection inclusive Profiler durations

ACTUAL is React's measured subtree render duration; BASE is an estimate, not an unoptimized comparison run. These are per-run callback medians, not end-to-end selection time. See the expanded methodology below.

Metric No memoization Manual memoization React Compiler
root ACTUAL (ms) 3.4 1.0 1.5
root BASE estimate (ms) 3.8 9.5 6.6
shell ACTUAL (ms) 3.4 0.9 1.5
shell BASE estimate (ms) 3.3 8.9 5.8
toolbar ACTUAL (ms) not observed not observed not observed
toolbar BASE estimate (ms) not observed not observed not observed
list ACTUAL (ms) 2.6 0.3 0.9
list BASE estimate (ms) 2.2 7.2 3.6
detail ACTUAL (ms) 0.5 0.7 0.6
detail BASE estimate (ms) 0.2 0.4 0.3
rows ACTUAL (ms) 0.0 0.0 0.0
rows BASE estimate (ms) 0.0 0.0 0.0
queueItems ACTUAL (ms) not observed not observed not observed
queueItems BASE estimate (ms) not observed not observed not observed
openButtons ACTUAL (ms) 0.0 0.0 not observed
openButtons BASE estimate (ms) 0.0 0.1 not observed
favoriteButtons ACTUAL (ms) 0.0 0.0 not observed
favoriteButtons BASE estimate (ms) 0.0 0.1 not observed

Why equal row counts do not mean equal work

Selection update events: No memoization: 67 rows, 67 open buttons, 67 favorite buttons; Manual memoization: 1 rows, 0 open buttons, 0 favorite buttons; React Compiler: 67 rows, 0 open buttons, 0 favorite buttons. Compiler and baseline record the same number of row events here, but the compiler records fewer child-button events. Equal row counts do not mean equal CPU cost or equal DOM work.

In this Oxc output, IncidentList caches the whole mapped result against incidents and selectedId. Invalidating that cache runs the map again. IncidentRow also caches child JSX internally, while the manual outer memo boundary can skip entering an unchanged row. This is a difference in optimization boundaries, not a general inability of React Compiler to achieve memo-like bailouts through caller-side JSX caching.

This explanation is scoped to oxc-transform-react 0.145.0 and the inspected measured output. Compiler App snapshot / Manual App snapshot are post-transform, pre-bundle inspection artifacts; later bundling and minification can change shipped code.

Compiler: list-wide cache

if ($[1] !== incidents || $[2] !== selectedId) {
    let t3;
    if ($[4] !== selectedId) {
      t3 = (incident) =>
        /* @__PURE__ */ _jsx(
          Profiled,
          {
            id: `row:${incident.id}`,
            children: /* @__PURE__ */ _jsx(IncidentRow, {
              incident,
              selected: selectedId === incident.id,
            }),
          },
          incident.id,
        );
      $[4] = selectedId;
      $[5] = t3;
    } else {
      t3 = $[5];
    }
    t2 = incidents.map(t3);
    $[1] = incidents;
    $[2] = selectedId;
    $[3] = t2;
  } else {
    t2 = $[3];
  }

Compiler: cached child JSX inside IncidentRow

if (
    $[6] !== incident.id ||
    $[7] !== incident.severity ||
    $[8] !== incident.title ||
    $[9] !== openIncident
  ) {
    t5 = /* @__PURE__ */ _jsx(OpenIncidentButton, {
      id: incident.id,
      title: incident.title,
      severity: incident.severity,
      onOpen: openIncident,
    });
    $[6] = incident.id;
    $[7] = incident.severity;
    $[8] = incident.title;
    $[9] = openIncident;
    $[10] = t5;
  } else {
    t5 = $[10];
  }

Manual: outer row boundary (ordinary builds define the row-memo flag as true)

const IncidentRow = __ROW_MEMO_ENABLED__ ? memo(IncidentRowImpl) : IncidentRowImpl;

Query filtering

Path: All incidents, 200 rows, All statuses, newest sort and no selected detail. Type A, then AP, then API; clear once to restore the initial list. This path runs independently of selection and favorite edits. Expected rows: 200 / 34 / 34 / 200.

Filtering responsiveness (normal production builds)

One complete warm-up, then 20 warmed cycles per app with rotated order, isolated contexts and 4x CDP CPU slowdown. Timing starts at the native input event before the React handler. AP and clear stop at the correct result-list mutation, followed by two frame callbacks. A and API leave result IDs unchanged: no result-DOM endpoint exists, so DOM timing is null, not zero; their input-to-two-frame opportunity still measures response work. Every endpoint verifies the exact ordered IDs and input value. This synchronous workload has no debounce or pending async search.

Typing uses at least 100 ms pacing between input steps; pacing, Playwright waits, setup and reset are excluded from the measured endpoints. Clearing is one input change, not repeated backspace. Medians and nearest-rank p90 are computed per step across 20 cycles; they are not summed into a workflow total. Raw typed-filtering trials.

Metric No memoization Manual memoization React Compiler
type A / DOM median (ms) no result change no result change no result change
type A / DOM p90 (ms) no result change no result change no result change
type A / Two-frame opportunity median (ms) 54.8 33.4 32.3
type A / Two-frame opportunity p90 (ms) 62.7 39.2 36.1
type AP / DOM median (ms) 35.8 29.5 29.3
type AP / DOM p90 (ms) 44.0 35.0 31.0
type AP / Two-frame opportunity median (ms) 62.3 53.9 54.1
type AP / Two-frame opportunity p90 (ms) 69.1 62.1 58.4
type API / DOM median (ms) no result change no result change no result change
type API / DOM p90 (ms) no result change no result change no result change
type API / Two-frame opportunity median (ms) 18.9 14.0 13.9
type API / Two-frame opportunity p90 (ms) 21.5 26.9 28.3
clear query / DOM median (ms) 62.2 55.6 55.2
clear query / DOM p90 (ms) 68.7 67.2 63.3
clear query / Two-frame opportunity median (ms) 243.9 238.6 231.5
clear query / Two-frame opportunity p90 (ms) 349.5 254.9 296.5

Filtering component work (profiling builds)

Fresh default state per profiling run, one warm-up and three native-speed repetitions with rotated order. Clearing remounts 166 previously removed rows; mounts are shown separately and are not counted as render-work update events. Inclusive ACTUAL/BASE callback medians below have diagnostic overhead, not production response timing.

type A (200 rows)

Metric No memoization Manual memoization React Compiler
Whole-app commits 1 1 1
Queue render-work events 4 0 0
Row render-work events 200 0 200
Open-button render-work events 200 0 0
Favorite-button render-work events 200 0 0
Row mounts (not update events) 0 0 0
Open-button mounts 0 0 0
Favorite-button mounts 0 0 0
Metric No memoization Manual memoization React Compiler
root ACTUAL (ms) 13.2 2.9 3.7
root BASE estimate (ms) 12.6 12.9 15.3
toolbar ACTUAL (ms) 0.3 0.4 0.0
toolbar BASE estimate (ms) 0.2 0.4 0.5
list ACTUAL (ms) 11.7 1.2 2.7
list BASE estimate (ms) 10.5 10.0 11.6

type AP (34 rows)

Metric No memoization Manual memoization React Compiler
Whole-app commits 1 1 1
Queue render-work events 4 0 0
Row render-work events 34 0 34
Open-button render-work events 34 0 0
Favorite-button render-work events 34 0 0
Row mounts (not update events) 0 0 0
Open-button mounts 0 0 0
Favorite-button mounts 0 0 0
Metric No memoization Manual memoization React Compiler
root ACTUAL (ms) 2.5 1.1 1.2
root BASE estimate (ms) 2.7 4.1 4.9
toolbar ACTUAL (ms) 0.2 0.3 0.1
toolbar BASE estimate (ms) 0.2 0.3 0.4
list ACTUAL (ms) 1.6 0.4 0.7
list BASE estimate (ms) 1.5 2.1 1.8

type API (34 rows)

Metric No memoization Manual memoization React Compiler
Whole-app commits 1 1 1
Queue render-work events 4 0 0
Row render-work events 34 0 34
Open-button render-work events 34 0 0
Favorite-button render-work events 34 0 0
Row mounts (not update events) 0 0 0
Open-button mounts 0 0 0
Favorite-button mounts 0 0 0
Metric No memoization Manual memoization React Compiler
root ACTUAL (ms) 2.6 1.1 0.7
root BASE estimate (ms) 2.6 4.0 4.3
toolbar ACTUAL (ms) 0.2 0.4 0.1
toolbar BASE estimate (ms) 0.2 0.4 0.4
list ACTUAL (ms) 1.8 0.2 0.4
list BASE estimate (ms) 1.3 1.9 1.5

clear query (200 rows)

Metric No memoization Manual memoization React Compiler
Whole-app commits 1 1 1
Queue render-work events 4 0 0
Row render-work events 34 0 34
Open-button render-work events 34 0 0
Favorite-button render-work events 34 0 0
Row mounts (not update events) 166 166 166
Open-button mounts 166 166 166
Favorite-button mounts 166 166 166
Metric No memoization Manual memoization React Compiler
root ACTUAL (ms) 18.3 17.8 17.6
root BASE estimate (ms) 7.7 8.9 10.8
toolbar ACTUAL (ms) 0.1 0.3 0.1
toolbar BASE estimate (ms) 0.1 0.3 0.3
list ACTUAL (ms) 17.8 17.2 17.4
list BASE estimate (ms) 6.6 6.9 8.0

Additional evidence

Component work and committed updates (median of 3 runs)

Additional actions come from the existing stateful workflow: mark all notifications read, Platform queue, selection, favorite, bulk search INC-0001, status filter, sort/reset, resolve. The inbox is opened before and closed after the measured notification acknowledgment. The bulk search is not the independent typed-query benchmark above; sort/reset combines four operations. No production latency is claimed for these additional actions.

read notifications

Metric No memoization Manual memoization React Compiler
Whole-app commits 1 1 1
Queue render-work events 0 0 0
Row render-work events 0 0 0
Open-button render-work events 0 0 0
Favorite-button render-work events 0 0 0
Row mounts (not update events) 0 0 0
Open-button mounts 0 0 0
Favorite-button mounts 0 0 0

switch queue

Metric No memoization Manual memoization React Compiler
Whole-app commits 1 1 1
Queue render-work events 4 2 4
Row render-work events 67 0 67
Open-button render-work events 67 0 0
Favorite-button render-work events 67 0 0
Row mounts (not update events) 0 0 0
Open-button mounts 0 0 0
Favorite-button mounts 0 0 0

favorite incident

Metric No memoization Manual memoization React Compiler
Whole-app commits 1 1 1
Queue render-work events 4 0 0
Row render-work events 67 1 67
Open-button render-work events 67 0 0
Favorite-button render-work events 67 1 1
Row mounts (not update events) 0 0 0
Open-button mounts 0 0 0
Favorite-button mounts 0 0 0

search (legacy bulk input)

Metric No memoization Manual memoization React Compiler
Whole-app commits 1 1 1
Queue render-work events 4 0 0
Row render-work events 1 0 1
Open-button render-work events 1 0 0
Favorite-button render-work events 1 0 0
Row mounts (not update events) 0 0 0
Open-button mounts 0 0 0
Favorite-button mounts 0 0 0

filter status

Metric No memoization Manual memoization React Compiler
Whole-app commits 1 1 1
Queue render-work events 4 0 0
Row render-work events 1 0 1
Open-button render-work events 1 0 0
Favorite-button render-work events 1 0 0
Row mounts (not update events) 0 0 0
Open-button mounts 0 0 0
Favorite-button mounts 0 0 0

sort and reset

Metric No memoization Manual memoization React Compiler
Whole-app commits 4 4 4
Queue render-work events 16 2 4
Row render-work events 136 0 136
Open-button render-work events 136 0 0
Favorite-button render-work events 136 0 0
Row mounts (not update events) 199 199 199
Open-button mounts 199 199 199
Favorite-button mounts 199 199 199

resolve incident

Metric No memoization Manual memoization React Compiler
Whole-app commits 1 1 1
Queue render-work events 4 0 0
Row render-work events 200 1 200
Open-button render-work events 200 0 0
Favorite-button render-work events 200 0 0
Row mounts (not update events) 0 0 0
Open-button mounts 0 0 0
Favorite-button mounts 0 0 0

Favorite interaction: post-GC JS heap (normal builds)

Separate favorite diagnostic, not load-window memory or selection/filtering allocation. The six UPLT contexts at 4x CPU slowdown also sample Performance.JSHeapUsedSize after forced GC at table readiness and after favoriting INC-0001. Medians of six paired runs; GC is outside load/CPU windows. This is renderer JS heap, not total tab/DOM/native memory or a leak test. Full runs remain in comparison.json.

Metric No memoization Manual memoization React Compiler
Before favorite (MiB) 4.67 4.72 5.09
After favorite (MiB) 6.09 5.11 5.59
Paired median change (KiB) 1451.4 402.0 502.0

Favorite interaction: CPU slowdown flame charts

One normal-build favorite trace per app at 4x CPU slowdown. JS-active sampled time excludes V8 (idle) and (program). It is an approximate slice of the sampled window, not end-to-end response time or a render count. Captures include Playwright-triggered work and cannot establish a performance winner. Each chart has its own time scale; compare measured values, not chart widths. Raw profiles open in Chrome DevTools.

Metric No memoization Manual memoization React Compiler
Sampled window (ms) 796.0 688.4 708.0
JS-active sampled time (ms) 639.4 543.5 546.5
JS-active share (%) 80.3 78.9 77.2

No memoization (1942 samples)

baseline CPU flame chart

Open raw No memoization CPU profile

Manual memoization (1653 samples)

manual CPU flame chart

Open raw Manual memoization CPU profile

React Compiler (1757 samples)

compiler CPU flame chart

Open raw React Compiler CPU profile

What the isolated row-memo experiment adds

The three comparison arms remain unchanged. A separate manual-app experiment toggles only the outer IncidentRow memo wrapper; callbacks, provider values and child memoization stay intact. Removing that wrapper does not reproduce compiler caching inside the row, and cannot predict the benefit of adding memo to the compiler arm.

These results match the main report's source fingerprint and runtime versions; the ON arm's initial-load assets are byte-identical to the measured manual build. Timings are from a separate paired experiment, not extra trials pooled into the three-arm selection comparison.

Action Row events ON / OFF Mean paired DOM saving (ms) 95% interval (ms) Mean paired frame-opportunity saving (ms) 95% interval (ms)
switch queue 0 / 67 3.21 0.44 to 5.98 3.98 -3.14 to 11.11
select incident 1 / 67 3.24 3.14 to 3.34 1.97 -0.84 to 4.78
favorite incident 1 / 67 2.82 2.15 to 3.49 1.96 0.63 to 3.30

Whole-build ON-minus-OFF bundle delta: +12 raw bytes / +1 gzip bytes. This is not the isolated library size of React.memo.

Positive savings are OFF minus ON, favoring memo. Thirty paired trials at 4x CPU throttle are grouped into three fresh-context blocks; exploratory Student-t intervals use the three block means, not 30 independent observations. All blocks share one browser/hardware session, and intervals are unadjusted across endpoints. Spanning zero is inconclusive, not equivalence. Frame opportunity is not actual paint; neither endpoint is INP. No automatic winner or timing gate is inferred.

Full row-memo report · Paired raw measurements · Computed ablation summaries

Methodology

Reading the Profiler durations

A render-work event marks an already-mounted component whose React 19.3.0 profiling fiber records PerformedWork in a completed update. A component may contribute events in multiple commits. Events are not milliseconds, equal-cost CPU operations, DOM mutations, or a complete function-invocation count. Only whole-app commits come from the external root React <Profiler>; nested callbacks do not prove the wrapped component ran. These internal diagnostics are not additive or an API guaranteed across React releases.

Root includes App and provider render bodies; shell, toolbar, list, detail, rows, queue items and buttons are inclusive subtree measurements. Overlapping root/parent/child durations are never summed into a total or treated as component self-time.

ACTUAL (actualDuration) is measured subtree render work. BASE (baseDuration) is React's estimated full-subtree render cost from the most recent measured component costs, not a separately measured unoptimized run. It may retain earlier costs and does not undo useMemo/compiler caches or reconstruct uncached calculation costs. BASE minus ACTUAL is not measured savings. Neither is event-handler, layout, paint or end-to-end interaction time.

Whole-app commits count one external root callback per [rootId, rootGeneration, commitSequence]. Initial mounts are outside action windows; action mounts stay separate from update events. Capture waits for semantic outcomes and completed batches in this synchronous workload, not arbitrary future asynchronous work.

In comparison.json, medianDurationMs (ACTUAL) and medianBaseDurationMs (BASE) are independently computed medians across per-run callback medians, not interaction totals. Mount summaries and raw runs stay separate; null means no matching callback was observed, while 0 is a measured zero. Raw durations and lifecycle identities remain in measurements.json. All Profiler durations include diagnostic overhead and are advisory, never CI timing thresholds. Native profiling is separate from normal-build 4x timings and browser sampling diagnostics.

Versions and provenance

Node v22.23.3; Chromium 145.0.7632.6; React 19.3.0; Vite 8.3.0; plugin-react 6.1.1; oxc-transform-react 0.145.0; Oxlint 1.85.0.

200 deterministic incidents; isolated Chromium contexts; one warm-up and 3 rotated profiling repetitions; 6 UPLT runs covering all app orders; initial mounts outside actions, action mounts reported separately from updates

Source, build and measurement fingerprints bind these observations to one capture. Missing or mismatched evidence fails publication rather than mixing results from different runs.