AcademyVue
Provide / Inject: Skipping the Prop Chain
provide() and inject() pass data straight to any descendant, no matter how deep — but only stay reactive when what you provide is itself reactive.
2 min readUpdated 3 exercises
Skipping every intermediate component in between
Passing data three or four components deep through props means every component in that chain has to declare and forward a prop it never actually uses itself — prop drilling. provide/inject skips straight from an ancestor to whichever descendant asks for it:
// Ancestor
import { provide, ref } from 'vue'
const theme = ref('light')
provide('theme', theme)
// Any descendant, arbitrarily deep
import { inject } from 'vue'
const theme = inject('theme', ref('light')) // second arg: default if not providedNothing in between has to know theme exists — only the provider and whichever consumers actually inject it.
Provide the reactive thing itself, never a value pulled out of it
const theme = ref('light')
provide('theme', theme.value) // a frozen snapshot — 'light', forever
provide('theme', theme) // the ref itself — stays livetheme.value is just the string 'light' at the exact instant provide runs — handing that string to descendants gives them no connection back to theme. This is the same mistake as passing a destructured reactive() property elsewhere in Vue: reactivity lives on the ref or the reactive object, not on a plain value read out of one.
String keys can drift apart; InjectionKey<T> cannot
provide('user', currentUser)
inject('User', 'Guest') // capital U — a different string, a different keyTwo string literals that are supposed to be the same key have no mechanism keeping them in sync — a typo on either side silently fails, falling back to inject's default with no error. A shared InjectionKey<T> constant removes the possibility entirely, because there is only one reference left to spell:
import type { InjectionKey } from 'vue'
const ThemeKey: InjectionKey<Ref<string>> = Symbol('theme')
provide(ThemeKey, theme)
inject(ThemeKey, ref('light'))As a bonus, in a real editor InjectionKey<T> also types the corresponding inject() call automatically — no manual inject<Ref<string>>(...) type argument needed once the key itself carries the type.
What interviewers check
Whether provide/inject gets reached for appropriately, not reflexively — it is the right tool for genuinely cross-cutting state (theme, locale, a design system's tokens), and the wrong one for anything only two or three components deep, where an explicit prop is more traceable and easier to test in isolation.
Now practice it
1 quiz · 2 drillsReading this page is the cheap half. These are the exercises that make you use it.
- Provide / InjectProprovide(key, ref.value) hands descendants a frozen snapshot instead of a live connection — fixed by providing the ref itself.DrillSolve · MediumDrillSolve · Medium
- Typed Provide/InjectProThe same pattern with a one-character key typo — fixed with a shared InjectionKey<T> that makes it structurally impossible.DrillSolve · HardDrillSolve · Hard
- Composition API · watch & Composablesprovide/inject is Composition API state sharing at its most direct — this set checks the surrounding fundamentals.QuizTake · MediumQuizTake · Medium
Read next
Slots: Passing Templates, Not Just Values
A slot is a function a parent hands its child — calling it renders the parent's markup, and calling it with data makes that markup scoped.
2 min
Ref vs Reactive: Vue's State Primitives
ref wraps any value behind a .value getter/setter; reactive proxies an object directly. The trade-offs explain why most Vue code reaches for ref by default.
2 min