codejump

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

Waiting at the end of this page.

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:

JavaScript
// 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 provided

Nothing 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

JavaScript
const theme = ref('light')

provide('theme', theme.value)  // a frozen snapshot — 'light', forever
provide('theme', theme)        // the ref itself — stays live

theme.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

TypeScript
provide('user', currentUser)
inject('User', 'Guest')  // capital U — a different string, a different key

Two 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:

TypeScript
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 drills

Reading this page is the cheap half. These are the exercises that make you use it.