Skip to content
← All questions
Intermediate

What is Provide/Inject?

ComponentsComposition API

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

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

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 user = 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 — 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.