In Vue 3, what do the onRenderTracked and onRenderTriggered hooks report, and what are their limits?
answer
- debug hooks, not lifecycle work
- read during render versus mutation
- target, type, key, values
- dev-only, per notification
basics
~20 sonRenderTracked fires for each reactive read the component's render records; onRenderTriggered fires when a mutation notifies the render effect. Both receive a debugger event with target, operation type and key. They run only in development and never during SSR.
solid answer
~40 s`onRenderTracked` and `onRenderTriggered` are **development-only debug hooks** registered in `setup()` (Options API: `renderTracked`, `renderTriggered`). `onRenderTracked` is called for **every reactive read** the render effect records — `type` is `get`, `has` or `iterate`, with the `target` object and `key`. `onRenderTriggered` is called when a **mutation notifies** the render effect — `type` is `set`, `add`, `delete` or `clear`, plus `key`, `newValue` and `oldValue`. The usual pattern is a `debugger` or a log in the callback. Limits: they do nothing in production builds and are not called during SSR; they report **causes, not durations**; several triggers can precede one re-render; and when the render reads a `computed`, the notification arrives through it without `target` or `key`, so you follow the chain with the computed's own `onTrigger` option.
go deeper
Recall that these are development-only hooks telling you what a component's render read and what change made it re-render.
Explain the event fields, the track and trigger operation types, and the Options API equivalents renderTracked and renderTriggered.
Use the hooks to trace an unexpected re-render to a specific write, including following the chain through computed values with their onTrigger option.
Treat these hooks as diagnosis tools inside a wider measuring practice, and keep performance fixes tied to what they actually showed.
## What the two hooks are Vue 3 components each have one **render effect**. While the render function runs, every reactive value it reads is recorded as a dependency; when one of those values changes, the effect is notified and the component is scheduled to re-render. The two debug hooks let you watch both halves of that from inside a component: - `onRenderTracked(cb)` — called when the render effect **records a dependency**. - `onRenderTriggered(cb)` — called when a dependency **notifies** the render effect that it changed. In the Options API the same hooks are the `renderTracked` and `renderTriggered` options. Both are marked **dev-only** in the API reference and are **not called during server-side rendering**. ## The event object Both callbacks receive a debugger event: | Field | Meaning | |---|---| | `effect` | The render effect itself | | `target` | The reactive object (or ref) involved | | `type` | For tracking: `get`, `has`, `iterate`. For triggering: `set`, `add`, `delete`, `clear` | | `key` | The property; a symbol for whole-object iteration | | `newValue` / `oldValue` | On triggers, the value change | | `oldTarget` | On clearing a `Map` or `Set`, the previous contents | A `type` of `iterate` while tracking tells you the render walked a **whole** object or collection — through `v-for` over it, `Object.keys`, or printing it — so any added or removed key will re-render the component. ## Using them ```vue <script setup lang="ts"> import { onRenderTracked, onRenderTriggered } from 'vue' onRenderTracked((e) => { console.log('tracked', e.type, e.key) }) onRenderTriggered((e) => { console.log('triggered', e.type, e.key, e.oldValue, '->', e.newValue) // debugger }) </script> ``` The docs recommend a `debugger` statement in the callback so you can inspect `e.target` interactively and walk up the call stack to the code that performed the mutation. ## Their limits 1. **Development only.** The runtime attaches these callbacks to the render effect only in development builds. In production they are never called. 2. **Not timing tools.** They say *what* was read or *what* changed, never how long rendering took. Pair them with the Vue devtools or `app.config.performance` for durations. 3. **Tracked is noisy.** The tracked callback fires for every recorded read on every render, including repeated reads, so log selectively. 4. **Triggered is per notification, not per render.** Two mutations in one tick produce two triggered events, then usually one re-render. And a notification can arrive even when the render ends up skipped — for example when a `computed` it reads recomputes to an equal value. 5. **Computed values hide the source.** When the render reads a `computed`, the trigger is relayed by that computed and the event carries only `effect`. Pass `computed(getter, { onTrack, onTrigger })` to see which source changed; `watch` and `watchEffect` accept the same `onTrack`/`onTrigger` options, also development-only. ## The same idea on other effects The render effect is not the only effect a component creates. The same debugging information is available elsewhere, also development-only: - `computed(getter, { onTrack, onTrigger })` — see what a computed depends on and which write invalidated it. In the Options API, computed debugging options are not available; use the `computed()` function. - `watch(source, cb, { onTrack, onTrigger })` and `watchEffect(fn, { onTrack, onTrigger })` — see why a watcher fired. - Options API watchers declared with the object syntax accept the same `onTrack` and `onTrigger` options. Together these let you follow a chain: a write triggers a computed, the computed notifies the render, the render re-runs. Each link can be observed with its own callback. ## When to reach for them - A component re-renders and you cannot see why from its template. - A component re-renders **too often** and you need to know which reactive write is responsible. - You suspect a render reads more than it needs — a whole object instead of one field. ## What they are not - They are not lifecycle hooks for doing work; nothing should depend on them in shipped code. - They do not catch updates caused by a parent passing new props unless the render read those props — and when it did, the event's `target` is the component's props object and `key` the prop name.
- A Vue 3 component's `onRenderTriggered` event has no `target` or `key`. What does that usually mean?The notification was relayed by a `computed` the render reads rather than coming straight from a reactive object. Add `onTrack`/`onTrigger` to that computed's debug options, `computed(getter, { onTrigger })`, to see which source changed; the event only carries the source when the render reads it directly.
- In a Vue 3 render, `onRenderTracked` reports `type: 'iterate'` on a large reactive object. Why does that matter for performance?The render walked the whole object — a `v-for` over it, `Object.keys`, or printing it — so it now depends on the object's shape as well as the fields it shows. Adding or deleting any key will re-render this component, even if the new key is never displayed.
saying these in an interview costs you the question
- Believes onRenderTriggered fires exactly once per re-render.
- Expects the hooks to report how long each render took.
- Leaves them in shipped code expecting them to run in production.
- Thinks onRenderTracked fires only once per dependency, ever.
- Assumes the event always names the original reactive source, even through a computed.