Metrics, profiling techniques, and lessons from GeekyAnts, Anthropic, Vercel, Atlassian, and Google.
A React AI application can load quickly and still feel slow.
The prompt field may hesitate while a response streams. A long answer may freeze the interface when syntax highlighting begins. A model may return text promptly, yet the browser may struggle to display it.
These are different performance problems. Measuring only API latency or a Lighthouse score leaves important gaps.
An effective measurement strategy covers three layers: page experience, AI response delivery, and browser rendering. Together, they explain where users wait and which changes actually improve the application.
1. Measure the Page and the AI Interaction Separately
Core Web Vitals remain a useful foundation for React applications:
| Metric | What it measures | Google’s “good” threshold |
|---|---|---|
| Largest Contentful Paint, LCP | Loading performance | 2.5 seconds or less |
| Interaction to Next Paint, INP | Interaction responsiveness | 200 milliseconds or less |
| Cumulative Layout Shift, CLS | Visual stability | 0.1 or less |
Google recommends assessing these thresholds at the 75th percentile, with mobile and desktop considered separately. Passing requires meeting the targets for all three metrics.
However, these metrics do not describe the entire AI interaction.
An interface can acknowledge a submit action quickly, producing a good interaction measurement, while the answer takes several seconds to arrive. Conversely, a response can begin promptly while repeated rendering makes typing or scrolling uncomfortable.
The application therefore needs additional, explicitly defined measurements:
| Application metric | Suggested definition | Problem it helps isolate |
|---|---|---|
| Composer readiness | Navigation until the prompt field accepts input | Initialization and hydration delays |
| First answer-text arrival | Submission until the first decoded answer-text delta reaches the client | End-to-end response startup |
| First answer committed | Submission until React commits the first answer text | Rendering delay after arrival |
| Inter-chunk gap | Time between decoded answer-text deltas | Uneven response delivery |
| Completion time | Submission until the application receives its completion signal | Total generation workflow duration |
| Cancellation latency | Cancel action until updates stop in the interface | Abort propagation and stale updates |
These are application-defined metrics, not standardized Web Vitals. Their names and boundaries should remain consistent across releases.
A status message such as “Searching documents” should also be measured separately from actual answer content.
2. Add Real User Monitoring
Real user monitoring reveals how the deployed application behaves across devices, browsers, networks, and conversation lengths.
Google’s web-vitals library provides callbacks for collecting LCP, INP, and CLS. Its documentation includes sending measurements to an analytics endpoint with navigator.sendBeacon().
A minimal browser-side example is:
import { onCLS, onINP, onLCP, type Metric } from "web-vitals";
function reportMetric(metric: Metric) {
const body = JSON.stringify({
id: metric.id,
name: metric.name,
value: metric.value,
rating: metric.rating,
release: "release-identifier",
});
navigator.sendBeacon(
"/api/performance",
new Blob([body], { type: "application/json" })
);
}
// Initialize once in the browser entry point.
onCLS(reportMetric);
onINP(reportMetric);
onLCP(reportMetric);The receiving endpoint must validate and store the events. Metric IDs can help handle updates or duplicate delivery according to the collection strategy.
Useful segmentation includes route, release, browser, device class, conversation-length bucket, and response format. A short plain-text answer and a large Markdown table should not disappear into one average.
Prompts and generated answers do not need to be included in performance telemetry.
3. Instrument the Response Lifecycle
The browser’s User Timing API supports custom performance marks and measurements. These entries can also appear in developer-tool timelines, making them useful for investigating application-specific events.
For each AI request, instrumentation should identify:
- Submission.
- First decoded answer text.
- First answer text committed to the interface.
- Completion, cancellation, or failure.
A unique request ID prevents concurrent responses from overwriting one another’s measurements. Marks should be cleared when no longer needed.
The distinction between arrival and display matters. An HTTP response chunk may contain protocol metadata, a tool event, partial encoding, or several text deltas. It is not necessarily a model token or a visible answer.
React commitment is also not an exact paint timestamp. Likewise, requestAnimationFrame() runs before a repaint, so it should not be presented as proof that the answer has already appeared on screen. Browser traces and frame screenshots provide stronger evidence when visual timing needs investigation.
Client timings should be correlated with server-side spans for retrieval, tool execution, and model generation through a shared request identifier. Durations recorded on separate machines should not be calculated by subtracting unsynchronized wall-clock timestamps.
4. Profile React Rendering During Streaming
A common streaming problem is excessive work per update.
Each new text delta may trigger Markdown parsing, code highlighting, message-list rendering, and unrelated sidebar updates. The response becomes longer while the amount of repeated work grows.
React’s <Profiler> helps identify expensive subtrees:
import { Profiler, type ProfilerOnRenderCallback } from "react";
const onRender: ProfilerOnRenderCallback = (
id,
phase,
actualDuration,
baseDuration
) => {
// Diagnostic example: aggregate samples in a real test harness.
console.debug({ id, phase, actualDuration, baseDuration });
};
export function ProfiledConversation() {
return (
<Profiler id="Conversation" onRender={onRender}>
<Conversation />
</Profiler>
);
}Here, Conversation represents the application’s own component.
actualDuration measures rendering work for the profiled subtree during an update. baseDuration estimates the cost of rendering the subtree without optimizations. Neither is a complete measurement of browser layout, painting, network latency, or user-visible completion. Profiling also adds overhead and requires a special profiling build for production-style profiling.
Measurements should compare equivalent workloads: the same response, stream cadence, conversation history, and device conditions.
React Performance Tracks can help connect component and scheduling activity with network requests and JavaScript execution on a shared browser timeline. They are available in supported development and profiling configurations.
5. Inspect Browser Work Beyond React
React rendering is only part of the frontend workload.
A component may render quickly while expensive layout, scrolling, syntax highlighting, or third-party code blocks the main thread.
Chrome’s documentation defines long tasks as tasks occupying the main thread for 50 milliseconds or longer. It also explains why individual long-task measurements can miss problematic frames made up of several shorter tasks. The Long Animation Frames API provides another diagnostic view where supported.
A useful browser recording includes:
- Typing while an answer streams.
- Scrolling through a long conversation.
- Rendering tables and highlighted code.
- Expanding citations or tool results.
- Cancelling and immediately submitting another request.
Average frame rate alone can hide disruptive freezes. Frame gaps, long tasks, layout activity, and interaction traces should be inspected together.
6. Use Repeatable Tests and Production Evidence
A stable benchmark should replay fixed responses with controlled chunk sizes and delays.
That separates frontend regressions from changes in model speed. A separate end-to-end test can then assess the real provider, retrieval system, and tools.
| Test scenario | Main measurement |
|---|---|
| Short answer, empty conversation | Response startup |
| Long answer, extensive history | Rendering growth |
| Code-heavy answer | Highlighting cost |
| Large table | Parsing and layout |
| Typing during generation | Responsiveness under load |
| Interrupted stream | Recovery and cancellation behavior |
Tests should run repeatedly under consistent CPU and network conditions. Release decisions should consider distributions and variability rather than one favorable run.
A standard Lighthouse navigation audit does not reproduce a complete interactive AI session. Total Blocking Time is useful as a laboratory diagnostic and proxy for responsiveness, while real user interaction measurements remain necessary. Google’s measurement guidance distinguishes these laboratory and field roles.
How Five Companies Approach Relevant Performance Problems
The following examples distinguish production reports, published implementation guidance, and proof-of-concept work. They do not establish a comparative ranking.
GeekyAnts: Tracking Latency Across a Multimodal Pipeline
GeekyAnts’ published real-time conversational AI proof of concept uses Next.js 13, React 18, WebRTC, and Pipecat. Its engineering article describes frame-level observability for stages such as speech recognition, LLM generation, and speech synthesis.
The transferable lesson is to measure individual stages alongside the overall experience.
For a React text application, that suggests separating retrieval, generation, transport, and rendering. The publication does not establish a company-wide Core Web Vitals monitoring practice or a production frontend benchmark.
Anthropic: Measuring Readiness and Streaming Smoothness
Anthropic’s engineering account describes optimizing specific Claude user journeys, including time until the page becomes typeable. It reports reducing that fresh-load measurement at the 75th percentile from 3.1 seconds to 0.55 seconds.
The same account describes investigating repeated subscriptions, style recalculation, code highlighting, and streaming-renderer work. Completed blocks were memoized, and tokenization work for growing code fences moved to a worker.
The lesson is that page readiness and streaming responsiveness deserve separate benchmarks. These are Anthropic-reported results for its own workloads.
Vercel: Avoiding Repeated Markdown Rendering
Vercel’s current AI SDK cookbook demonstrates Streamdown for streamed Markdown. It documents handling incomplete Markdown and memoizing blocks so unchanged content can skip re-rendering.
This is published implementation guidance rather than a benchmark across all Vercel applications. Teams can evaluate the pattern by measuring rendering duration and browser blocking time with identical streamed responses.
Atlassian: Measuring Visibility, Interactivity, and Hydration
Atlassian’s Confluence engineering article describes tracking First Contentful Paint, visual completion, interactivity, and hydration success. It reports approximately 40% improvement in First Contentful Paint after introducing React streaming server-side rendering.
This is a React page-loading example, not an LLM response benchmark. Its relevance is the measurement approach: early visibility, usable controls, and successful hydration are distinct outcomes.
Google: Standardizing Field Measurement
Google provides Core Web Vitals guidance and the open-source web-vitals library for collecting browser experience metrics. These establish a consistent baseline alongside application-specific AI timings.
This example concerns public tooling and measurement methodology, rather than an undocumented claim about Google’s internal AI interfaces.
Turn Measurements Into Release Decisions
A useful performance report should connect each symptom with its likely owner.
If answer-text arrival is slow while rendering remains smooth, investigation should focus upstream. If arrival is prompt but typing deteriorates during streaming, browser work deserves attention. If only long conversations regress, the test should examine how work grows with history.
The goal is an interface that becomes usable promptly, displays responses steadily, and remains responsive throughout the task. Measuring those outcomes separately gives React teams evidence for deciding what to improve next.





















Add Comment