Advanced Reactivity: shallowRef and triggerRef
shallowRef for performance, markRaw to exclude from reactivity, triggerRef for manual trigger.
Advanced Reactivity: shallowRef and triggerRef is a free Vue Academy lesson on CoddyKit — lesson 1 of 4. You can read the complete lesson below for free — then practise it hands-on in the browser with a built-in code editor and a 24/7 AI tutor. It is part of the Vue Academy learning path, one of 4 lessons in the course, and your progress syncs across the web and the CoddyKit app.
Beyond ref and reactive
Vue’s deep reactivity is convenient but not always what you want. For large objects or external instances, deep tracking is wasteful. Vue offers shallowRef, triggerRef, and markRaw for fine control.
How ref Tracks Deeply
A normal ref holding an object makes the entire object deeply reactive — mutating any nested property triggers updates. Great for small state, costly for big structures.
import { ref } from "vue"
const user = ref({ name: "Ada", prefs: { theme: "dark" } })
user.value.prefs.theme = "light" // triggers update (deep)shallowRef: Only .value Is Reactive
shallowRef tracks reassignment of .value but does not track mutations inside the held value. Inner changes do not trigger renders.
import { shallowRef } from "vue"
const state = shallowRef({ count: 0 })
state.value.count++ // does NOT trigger update
state.value = { count: 1 } // DOES trigger (new .value)When to Use shallowRef
Use it for large immutable-ish data you replace wholesale, or to wrap external objects (a chart instance, a big dataset) where deep tracking adds overhead with no benefit.
const chart = shallowRef(null)
// chart.value = new Chart(...) replaces wholesaleThe Deep Mutation Problem
Sometimes you DO mutate inside a shallowRef (for performance) but still need a re-render at a controlled moment. Vue cannot see the change, so the UI goes stale.
const list = shallowRef([1, 2, 3])
list.value.push(4) // mutated, but no update firestriggerRef: Force an Update
triggerRef manually forces effects depending on a shallow ref to re-run, after you have mutated its contents. You batch many mutations, then trigger once.
import { shallowRef, triggerRef } from "vue"
const list = shallowRef([1, 2, 3])
list.value.push(4)
list.value.push(5)
triggerRef(list) // now the UI updatesA Performance Pattern
The pattern: hold a big structure in shallowRef, mutate it freely without per-change reactivity cost, then call triggerRef once when you want the view to reflect the batch.
function bulkUpdate(rows) {
for (const r of rows) data.value.push(r) // cheap
triggerRef(data) // one update
}markRaw: Opt Out Entirely
markRaw marks an object so Vue will never make it reactive, even inside a reactive or normal ref. Use it for third-party class instances that should not be proxied.
import { markRaw, reactive } from "vue"
const state = reactive({
map: markRaw(new ThirdPartyMap()),
})
// state.map is the raw instance, never a proxyWhy markRaw Matters
Wrapping complex class instances (maps, editors, websockets) in a reactive proxy can break their internal references or cause subtle bugs. markRaw keeps them untouched while still living inside reactive state.
Combining the Tools
These compose: a shallowRef holding a markRaw instance gives you a slot you can replace reactively, with an inner object Vue never proxies — ideal for external integrations.
const editor = shallowRef(null)
function init(el) {
editor.value = markRaw(new CodeEditor(el))
}Reactivity Cost vs Control
The trade-off: deep reactivity is effortless but heavier; shallow APIs give you control and performance at the cost of triggering updates yourself. Reach for them only when profiling shows a need.
Quick Check
Check your understanding of advanced reactivity.
Recap
You learned advanced reactivity controls:
shallowRefreacts only to.valuereassignment, not inner mutations — good for large or external data.triggerRefmanually forces updates after mutating a shallow ref’s contents (batch then trigger once).markRawpermanently opts an object out of reactivity, ideal for third-party class instances.- These give performance and control at the cost of triggering updates yourself.
Frequently asked questions
Is the “Advanced Reactivity: shallowRef and triggerRef” lesson free?
Yes — the full text of “Advanced Reactivity: shallowRef and triggerRef” is free to read here on the web, and the Vue Academy course includes 4 lessons in total. To practise it interactively (a built-in code editor and a 24/7 AI tutor) and unlock the rest of the Vue Academy course, upgrade to CoddyKit PRO.
What will I learn in “Advanced Reactivity: shallowRef and triggerRef”?
shallowRef for performance, markRaw to exclude from reactivity, triggerRef for manual trigger. You practise Vue Academy with hands-on code you run directly in the browser, and a 24/7 AI tutor answers your questions as you work through the lesson.
Do I need any experience to start Vue Academy?
No prior experience is required. Vue Academy on CoddyKit is structured for beginners through advanced learners; this is — lesson 1 of 4, so you can start here or from the beginning and move at your own pace.
How long does the “Advanced Reactivity: shallowRef and triggerRef” lesson take?
Most CoddyKit lessons take about 5–10 minutes. Each one is bite-sized and interactive, so you make steady progress and pick up exactly where you left off across the web and the app.
Can I write and run code in this Vue Academy lesson?
Yes. Every Vue Academy lesson includes a built-in code editor, so you write and run real code right in your browser and get instant AI feedback — no local setup required.
All lessons in this course
- Advanced Reactivity: shallowRef and triggerRef
- Composable Factories and Parameterized Composables
- State Machines with XState in Vue
- effectScope for Grouped Effects