Provide / Inject
|
This section documents the current Vue 3.x release line as published at
the official Vue.js documentation, which is the reference these pages are written and
verified against. No specific patch version is pinned. The Composition API with This content was generated with the assistance of AI and should be verified against the official documentation before being relied on in production, since Vue and its ecosystem iterate quickly. This section’s bibliography lists the reference material consulted while preparing these pages. |
provide and inject let an ancestor act as a dependency provider for its whole subtree: any descendant,
however deep, can ask for a value by key without every component in between forwarding it as a prop. This page
covers the API, typing it, keeping the shared value reactive, and where a store is the better tool.
The problem: prop drilling
When only a deep descendant needs a value, passing it as a prop forces every component on the path to accept
and re-pass it. That noise is prop drilling — the left side of the figure. provide / inject removes the
intermediate hops.
provide and inject
The ancestor calls provide(key, value) in its <script setup>:
<script setup>
import { provide, ref } from 'vue'
const theme = ref('dark')
provide('theme', theme)
</script>
Any descendant calls inject(key):
<script setup>
import { inject } from 'vue'
const theme = inject('theme') // the same ref the ancestor provided
</script>
Both must run synchronously during setup. See Provide / Inject.
App-level provide
app.provide() registers a value visible to every component the app renders. It is the natural home for
app-wide configuration and also works from inside a plugin:
import { createApp } from 'vue'
const app = createApp(App)
app.provide('apiBase', '/api/v1')
See app.provide().
Injection keys
A string key works, but a Symbol avoids collisions across a large app or a library. In TypeScript, typing
that symbol as InjectionKey<T> makes both ends type-safe: provide checks the value and inject infers the
result.
// keys.ts
import type { InjectionKey, Ref } from 'vue'
export interface UserSession {
id: string
name: string
}
export const sessionKey = Symbol('session') as InjectionKey<Ref<UserSession | null>>
// provider -- type error unless `session` is Ref<UserSession | null>
provide(sessionKey, session)
// consumer -- inferred as Ref<UserSession | null> | undefined
const session = inject(sessionKey)
Default values
inject returns undefined when no provider is found (and warns in development). Pass a second argument as
the fallback; pass a factory plus true as the third argument when the default is expensive or must not be
shared between consumers:
const theme = inject('theme', 'light') // simple default
const list = inject('list', () => reactive([]), true) // factory default
Keeping injected values reactive
Provide a ref, a reactive object, or a computed — not a plain unwrapped value — so that when the
ancestor updates it, every injecting descendant re-renders:
<script setup>
import { provide, reactive, readonly } from 'vue'
const store = reactive({ count: 0 })
function increment() {
store.count++
}
// hand out a read-only view plus an explicit mutator, so descendants
// cannot mutate the shared state directly
provide('counter', {
state: readonly(store),
increment
})
</script>
Keeping mutations inside the providing component — and passing readonly() outward — keeps the data flow
traceable. See
Working with Reactivity.
provide/inject vs. a store
provide / inject is a tree-scoped tool: it shines for a widget family that shares context (a form and
its fields, a tab group, a design-system theme) and for skipping a couple of prop hops. It is not a general
application state container — no devtools timeline, no cross-tree access, and testing a consumer means
mounting it under a provider.
When state is genuinely global, mutated from many unrelated places, or needs tooling and hot-module replacement, use a store — in the Vue ecosystem that is Pinia; see State Management for the comparison. Angular’s hierarchical injector solves the same tree-scoping problem — see Angular Reference.
See also
-
Registration and Props — props and the drilling problem provide/inject addresses.
-
State Management — Pinia and the
reactive+providestore-lite pattern. -
Composables — wrapping
injectin a composable for a typed, reusable consumer API.