# What gets traced


Once `enableTracing` is on, the browser client records the following spans on its own. You don't write any code for these:

| Span | When |
| --- | --- |
| `browser_pageload` | The first load of a page |
| `browser_navigation` | A client side route change |
| `browser_fetch` | A `fetch()` call |
| `browser_xhr` | An `XMLHttpRequest` |

On top of these spans, the client measures the five Core Web Vitals for every page load and puts them on the `browser_pageload` span. See [web vitals](/docs/javascript/tracing/web-vitals).

A `browser_pageload` or `browser_navigation` span is always the root of its trace. Every `browser_fetch` and `browser_xhr` span your page makes while that root is open becomes a child of it.

The plain JavaScript client already opens a `browser_navigation` span for a client side route change on its own, by watching for history and URL changes. It has no router, so it only knows the URL. A framework package with router support adds the matched route pattern, as described below.

## What does not count as a navigation

The plain JavaScript client decides a navigation happened by comparing the **path** of the URL. Only the path, not the query string and not the hash. So these open no `browser_navigation` span:

| Change | Example | New span? |
| --- | --- | --- |
| The path changes | `/products` → `/products/12` | Yes |
| Only the query string changes | `/products` → `/products?page=2` | No |
| Only the hash changes | `/products` → `/products#reviews` | No |

That's on purpose. A hash change usually just scrolls to a section, and `?page=2` is the same page with a different filter. Counting either as a page view would fill your Pages table with duplicates.

Framework packages override this behaviour. Their router decides what counts as a navigation, and they all open a `browser_navigation` span when the query string changes. Hash changes depend on the router: SvelteKit opens no span, Vue Router does.

## What needs a framework integration

Two things need a framework package on top of `@flareapp/js`:

**Route patterns.** On its own, a `browser_navigation` span only knows the URL it navigated to. A framework's router integration adds the matched route pattern, so navigations to the same route group together on the Pages table instead of becoming one entry for every URL. When you open a single trace, you still see the URL. Flare uses the pattern to group pages, not to name the span. See the [React](/docs/react/tracing/introduction), [Vue](/docs/vue/tracing/introduction), or [Svelte](/docs/svelte/tracing/introduction) tracing introduction for how each framework package sets this up.

**Component mount spans.** Timing how long a component takes to mount is not something a browser API exposes on its own. It needs a framework package that hooks into that framework's component lifecycle. These spans use the `browser_component` span type. See [how component profiling works](/docs/javascript/profiling/how-component-profiling-works) for what that looks like, then the [React](/docs/react/profiling/introduction), [Vue](/docs/vue/profiling/introduction), or [Svelte](/docs/svelte/profiling/introduction) profiling introduction for how each framework package sets it up.

## Timing your own code

For anything that isn't covered above, for example a slow client side calculation, write a manual span. See [manual spans](/docs/javascript/tracing/manual-spans).
