Repository navigation
Description
Is there an existing issue for this?
- I have checked for existing issues https://github.com/getsentry/sentry-javascript/issues
- I have reviewed the documentation https://docs.sentry.io/
- I am using the latest SDK release https://github.com/getsentry/sentry-javascript/releases (we're on 10.73.0;
maybeEndRootComponentSpaninpackages/vue/src/tracing.tsis unchanged ondevelop)
How do you use Sentry?
Sentry Saas (sentry.io)
Which SDK are you using?
@sentry/nuxt (the code in question is in @sentry/vue)
SDK Version
10.73.0
Framework Version
Vue 3.5.42, Nuxt 4.5.2
Link to Sentry event
No response
Reproduction Example/SDK Setup
Default Vue integration, tracing on, component tracking left off:
import { createApp, h } from "vue";
import * as Sentry from "@sentry/vue";
const Leaf = { render: () => h("span", "x") };
const App = {
render: () => h("div", Array.from({ length: 1000 }, () => h(Leaf))),
};
const app = createApp(App);
Sentry.init({ app, dsn: "<dsn>", tracesSampleRate: 1.0 });
let timers = 0;
const setTimeoutOriginal = window.setTimeout;
window.setTimeout = (handler, delay, ...args) => {
if (delay === 2000) timers += 1;
return setTimeoutOriginal(handler, delay, ...args);
};
app.mount("#app");
console.log(timers); // 2003Steps to Reproduce
- Enable tracing in a Vue 3 app (any
tracesSampleRate) and leavetrackComponentsat its default offalse. - Render a view with many components, for example a table with a component per cell.
- Count
setTimeoutandclearTimeoutcalls during the mount.
Expected Result
With component tracking off, render work should cost the SDK roughly the same no matter how many components mount: one debounce timer for the root Application Render span, pushed back while render activity continues.
Actual Result
Every untracked component calls maybeEndRootComponentSpan(this, …) from both of its mount hooks, and the timer handle is stored on that component (vm.$_sentryRootComponentSpanTimer) rather than on the root. The debounce therefore never coalesces across components:
| Components mounted | setTimeout(…, 2000) calls |
clearTimeout calls |
|---|---|---|
| 100 | 203 | 102 |
| 1,000 | 2,003 | 1,002 |
That leaves one pending timer per component. All of them fire about two seconds after the render, and each is a no-op once the first has ended the root span.
On our heaviest page, a review table that mounts thousands of components, this came to about 10,000 timer calls and roughly 60 ms of main-thread time in a Chrome CPU profile, plus the burst of callbacks two seconds later, which can land on the user's first interaction. trackComponents and hooks don't avoid it: the !shouldTrack branch still calls maybeEndRootComponentSpan, and DEFAULT_HOOKS is always included.
Additional Context
Keeping a single timer on $root would make this constant per render, for example:
- store the latest activity timestamp on
vm.$rooton every hook (a property write); - start one timer on the root if none is pending;
- when it fires, end the span at the stored timestamp if no activity happened within
timeout, otherwise re-arm it for the remaining time.
That would also end Application Render at the last render activity. Today, because each component keeps its own timer, it looks like whichever timer fires first ends the root span with its component's timestamp, which can be before the render finished.
Possibly related: #23375, about the Vue integration's reliance on app.mixin() under Nuxt 5. createTracingMixins on develop still has the per-component timer.
Metadata
Metadata
Assignees
Labels
Projects
- StatusShow more project fieldsWaiting for: Product Owner
Activity
linear-code commentedon Sep 30, 2026
JS-3852
samiashi commentedon Sep 30, 2026
Hi @s1gr1d, I noticed you picked this up. I've already opened a fix in #24864. Happy to adjust it to whatever approach you had in mind, or to hand it over entirely.
s1gr1d commentedon Sep 30, 2026
Oh, I only noticed your PR now 😬
samiashi commentedon Sep 30, 2026
No worries at all! Thanks for picking this up so quickly
fix(vue): Share one root render span debounce timer across components (…
github-actions commentedon Oct 1, 2026
A PR closing this issue has just been released 🚀
This issue was referenced by PR #24868, which was included in the 11.2.0 release.
fix(v10/vue): Share one root render span debounce timer across compon…