Skip to content
← All questions
⚡ Intermediate

What is Provide/Inject?

🧩 Components🔗 Composition API🍍 Pinia💉 Provide/Inject

When a parent component needs to pass data to a deeply nested child, you'd normally have to pass props through every component in between, even if intermediate components don't use the data. This is called "prop drilling," and it makes your code fragile and hard to maintain.

provide and inject solve this. A parent provides data, and any descendant, no matter how deep, can inject it directly without any intermediate component knowing about it.

How it works

vue
<!-- GrandParent.vue -->
<script setup lang="ts">
import { provide, ref } from 'vue'

const theme = ref('dark')
provide('theme', theme)
</script>
Open in Vue Playground
vue
<!-- DeeplyNestedChild.vue (any depth below GrandParent) -->
<script setup lang="ts">
import { inject } from 'vue'

const theme = inject('theme') // 'dark' — no props passed through middle components
</script>
Open in Vue Playground

The provided value is reactive. When theme changes in the parent, every component that injected it updates automatically.

Tree diagram showing provide/inject bypassing intermediate components

Type-safe injection with InjectionKey

String keys work but don't give you type safety. Use InjectionKey for typed provide/inject:

ts
// keys.ts
import type { InjectionKey, Ref } from 'vue'

interface User {
  name: string
  role: string
}

export const UserKey: InjectionKey<Ref<User>> = Symbol('user')
ts
// Provider
import { provide, ref } from 'vue'
import { UserKey } from '@/keys'

const user = ref<User>({ name: 'John', role: 'admin' })
provide(UserKey, user)
ts
// Consumer
import { inject } from 'vue'
import { UserKey } from '@/keys'

const user = inject(UserKey) // Ref<User> | undefined
const userWithDefault = inject(UserKey, ref({ name: 'Guest', role: 'viewer' })) // with default

When to use it (and when not to)

Good use cases:

  • Theme or locale shared across an entire app
  • Auth/user state accessible deep in the tree
  • Table or form context (a <Table> provides column config, child <TableCell> injects it)
  • Plugin-style features (a toast manager, a modal manager)

Bad use cases:

  • Passing data between siblings. provide/inject is parent-to-descendant only
  • Replacing all props with inject. It makes components harder to test and understand because their dependencies are implicit
  • Global state that many unrelated components read and write. Use Pinia instead

Provide/Inject vs Props vs Pinia

PropsProvide/InjectPinia
DirectionParent → child (1 level)Ancestor → any descendantAny component → any component
ExplicitYes (visible in template)No (implicit dependency)Somewhat (import store)
ReactiveYesYesYes
Best forDirect parent-child dataSubtree-wide contextGlobal app state

See also: What is getCurrentInstance() and why should you avoid it? · What is a composable?

References

Released under the MIT License.