If you’ve ever been stuck debugging a slow application you may have wondered: Where do I start looking first—the backend or the frontend?
For most teams, observability ends once they reach the backend. We have distributed tracing, logs, and metrics that provide a crystal clear view of all microservices. But the frontend? It is often treated like a black box.
We know when a request hit the server and it flowed through our services, but we do not know what happens prior to that—the duration of setup done by the UI, whether the user saw the request returned smoothly, etc.
That is exactly why frontend tracing is so important. By instrumenting user interactions in the browser with something like OpenTelemetry (OTel), developers can connect spans from the frontend and backend into a single trace.
The benefit? Full-stack visibility with quicker performance debugging.
Why does the Frontend need Tracing
Frontend systems have their own differences that may not be visible in backend logs:
- User interaction latency - When a user clicks a button and it feels "slow". Was there too much React re-rendering? Was the JS bundle too heavy? Was the API slow? Without tracing, we simply don't know.
- Asynchronous complexity - Single-page apps can have multiple requests concurrently along with rendering tasks and state transitions all happening at the same time. Without tracing, it is akin to trying to untwist a ball of spaghetti.
- Local differences - Backend logs don't know if the user was on a throttled 3G connection, how much CPU was being used, or if there was any funky behaviour with the user's browser.
Frontend tracing brings all of this together and allows us to:
- Connect the user action (click, navigation, or input event) to network requests that got fired.
- Follow the requests through the backend services.
- Measure and compare perceived latency (what the user thinks) to system latency (what the backend is using).
Once we pull all of this together, discovering bottlenecks is simply a visibility problem. We can see clearly, and debugging isn't a guessing game anymore.
A Quick Review: How Tracing works
Before moving on to the frontend, let's review some basics.
- Trace: A trace is the entire execution path, e.g. "User clicked checkout."
- Span: A span is a smaller piece of work do doen in that trace (UI render, API call, DB query).
Each trace has a unique id that is propagated across boundaries in the system (via headers like traceparent).
Walkthrough Example
- User clicks "Buy Now" in your React app → frontend span starts.
- That click makes a fetch to /checkout → OTel automatically created a network span.
- It's going to send the request with the headers containing the trace id.
- The backend will continue the trace and then also create spans for order processing, DB writes, etc.
- Observational tools like Jaeger, Tempo, or Datadog will show you the entire trace journey from end-to-end.
All of a sudden the frontend is no longer a black box, it is part of the distributed system narrative.
Setting Up OpenTelemetry in the Browser
Setting up OTel in the frontend is ridiculously easy.
Step 1: Install Dependencies
npm install @opentelemetry/sdk-trace-web @opentelemetry/instrumentation-fetch @opentelemetry/exporter-trace-otlp-http
Step 2: Bootstrap within your App
typescript
import { WebTracerProvider } from '@opentelemetry/sdk-trace-web';
import { BatchSpanProcessor } from '@opentelemetry/sdk-trace-base';
import { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-http';
import { registerInstrumentations } from '@opentelemetry/instrumentation';
import { FetchInstrumentation } from '@opentelemetry/instrumentation-fetch';
const provider = new WebTracerProvider();
const exporter = new OTLPTraceExporter({
url: 'http://localhost:4318/v1/traces',
});
provider.addSpanProcessor(new BatchSpanProcessor(exporter));
provider.register();
registerInstrumentations({
instrumentations: [new FetchInstrumentation()],
});
console.log('OpenTelemetry bootstrapped');
This means:
- That every fetch or XHR call is traced.
- That spans are sent to your trace collector (OTLP, Jaeger, Datadog, etc...).
Context is propagated downstream for you as a developer to developing application logic without managing context.
Recording User Events
It’s nice to have network requests, but tracing becomes significantly more valuable when you are able to capture the user events. You can then correlate:
typescript
import { trace } from '@opentelemetry/api';
const tracer = trace.getTracer('ui-tracer');
document.getElementById('checkout-btn')?.addEventListener('click', () => {
const span = tracer.startSpan('user.click.checkout');
setTimeout(() => {
span.end();
}, 200);
});
“User clicked checkout”
- → The triggered API call
- → The backend order service
- → The database query
You can simply put the whole thing in hooks or higher-order components in React to ensure your tracing is consistent throughout your application.
Connecting the Frontend and Backend
This is where the magic programmatically happens, when spans cross over boundaries.
- OTel will inject the traceparent header automatically.
- When the backend services receive the request, they will pick it up and continue the trace.
- Your trace viewers will stitch all of these together to create a unified timeline.
An example trace in Jaeger could look like this:
`pqsql
user.click.checkout (Frontend, 50ms)
└── fetch /checkout (Frontend, 20ms)
└── OrderService.processOrder (Backend, 120ms)
└── Database query (DB, 80ms)
With this view, it is very clear where the slowdown on the request occurs, whether on the client, in the API, or deeper in the database.
Real World Debugging & Monitoring
Debugging Bugs that are Difficult to Reproduce
Ever had a user say, "Checkout is slow," and you can't reproduce it? With tracing you can find out if the issue was:
- Slower time parsing the JavaScript bundles
- A slow API call
- Or something from the backend DB
Performance Monitoring
Traces shows us:
- Render time vs. backend processing time
- Long tasks blocking interactivity
- Connections between front-end measurement (Time to Interactive) and backend API latency
Reliability Insights
Taking traces with samples over time can show you:
- Flaky APIs that only fail under specific UI paths
- Slowdowns could be limited to a specific browser
Looks at device specific slowdowns
Beyond OpenTelemetry: Other Tools
OpenTelemetry is a widely used open standard that has several popular vendor specific tools that are not part of the OpenTelemetry specification that you can use.
- Sentry Performance - A Performance tool with transaction tracing with very little setup.
- Datadog RUM - A Real User Monitoring tool that combines RUM with tracing the backend.
Other vendor tools can be easier to use and implement than OpenTelemetry; OpenTelemetry can allow for portability in the future.
Frontend Observability Best Practices
- Sample Wisely
You don't need to trace every single click. Swerve would recommend using probabilistic sampling to find the right level of visibility with manageable overhead. - Don't Capture Sensitive Data
You should never capture dare raw keystrokes, passwords, or personal information. Always make sure you sanitize the spans. - Communicate Strategies Across Stacks
Observability can only work if both the frontend and backend teams have some level of alignment here (Pro Tip: OTel everywhere). - Provide Context for Visualizations
A flame graph doesn't mean much if you can't relate the user experience metrics (like LCP, TTI) with the backend spans.
Conclusion: The Black Box is Closed
For way too long, performance at the frontend was an afterthought in observability. But look out! Users don't care about API response times alone. They only experience the end-to-end logical journey (i.e. clicking a button, waiting for a screen to load, and seeing the result).
Foreground tracing (potentially with something like OpenTelemetry) will finally give us backend-style observability at the UI.
This means:
- Connecting user interactions to backend requests
- Debugging a distributed system as a whole
- Delivering better, predictable user experiences
In other words, tracing is not just a nice-to-have or value-add for observability: it is the missing link to full-stack observability.
Frequently Asked Questions
Q1: Isn't tracing too heavy for frontend apps?
Through sampling, definitely not. You don't need every trace--you need enough traces to recognize patterns.
Q2: Do I need to make changes to the backend to trace on the frontend?
In a perfect world, yes. In order to stitch traces together, your backend also needs to have OTel or something that is compatible.
Q3: Will tracing replace logging and metrics?
No. Tracing is not replacing logging and metrics. Logs gives you all the detail, metrics give you trends, and traces give you the story across systems.
Q4: What if my team is already using Sentry or Datadog?
That's OK! Many vendor tools come with tracing out-of-the-box. You can still bring in OTel to avoid vendor lock-in in the future.
👉 Now I'm curious: If you could trace any one thing in your frontend app today, what would you trace? That button click that always seem to lag? That mysterious network request? Or, maybe that rendering phase that seems to hang forever when using older devices?
That's the beauty of tracing: it sheds light on exactly what you need it to.