Skip to main content
Practice Problems

Provide/inject in Vue.js

What is provide/inject?

provide and inject are a pair of APIs for passing data from a parent component to any descendant without explicitly passing through props at each level.

This solves the "prop drilling" problem - when you need to pass props through many components.


Basic Usage

Composition API

javascript
<!-- App.vue (parent) --> <template> <ChildComponent /> </template> <script setup> import { provide, ref } from 'vue' import ChildComponent from './ChildComponent.vue' const theme = ref('dark') provide('theme', theme) </script>
javascript
<!-- GrandchildComponent.vue (descendant) --> <template> <div :class="theme"> Theme: {{ theme }} </div> </template> <script setup> import { inject } from 'vue' const theme = inject('theme') </script>

Reactivity

Passing Reactive Data

javascript
<!-- App.vue --> <template> <button @click="theme = theme === 'dark' ? 'light' : 'dark'"> Toggle Theme </button> <ChildComponent /> </template> <script setup> import { provide, ref } from 'vue' const theme = ref('dark') // Pass ref, not value provide('theme', theme) </script>
javascript
<!-- DeepChild.vue --> <template> <div :class="theme"> Current theme: {{ theme }} </div> </template> <script setup> import { inject } from 'vue' const theme = inject('theme') // theme will automatically update on change </script>

Passing Functions for Modification

javascript
<!-- App.vue --> <script setup> import { provide, ref } from 'vue' const count = ref(0) const increment = () => { count.value++ } provide('count', count) provide('increment', increment) </script>
javascript
<!-- DeepChild.vue --> <template> <p>Count: {{ count }}</p> <button @click="increment">Increment</button> </template> <script setup> import { inject } from 'vue' const count = inject('count') const increment = inject('increment') </script>

Default Values

javascript
<script setup> import { inject } from 'vue' // If 'theme' not provided, use 'light' const theme = inject('theme', 'light') // Default value as function const user = inject('user', () => ({ name: 'Guest' })) // Third parameter - value is computed as factory const config = inject('config', () => { return { /* complex logic */ } }, true) </script>

Symbol Keys

Using Symbol prevents name conflicts:

javascript
// keys.js export const ThemeKey = Symbol('theme') export const UserKey = Symbol('user')
javascript
<!-- App.vue --> <script setup> import { provide, ref } from 'vue' import { ThemeKey, UserKey } from './keys' const theme = ref('dark') const user = ref({ name: 'John' }) provide(ThemeKey, theme) provide(UserKey, user) </script>
javascript
<!-- Child.vue --> <script setup> import { inject } from 'vue' import { ThemeKey, UserKey } from './keys' const theme = inject(ThemeKey) const user = inject(UserKey) </script>

Application-level provide

javascript
// main.js import { createApp } from 'vue' import App from './App.vue' const app = createApp(App) app.provide('apiUrl', 'https://api.example.com') app.provide('appName', 'My App') app.mount('#app')
javascript
<!-- AnyComponent.vue --> <script setup> import { inject } from 'vue' const apiUrl = inject('apiUrl') const appName = inject('appName') console.log(apiUrl) // 'https://api.example.com' console.log(appName) // 'My App' </script>

provide/inject vs props

When to use props

javascript
<!-- Direct parent-child relationship --> <ChildComponent :title="title" :count="count" />

Use props when:

  • Components are directly related
  • Need explicit data connection
  • Simple hierarchy (1-2 levels)

When to use provide/inject

javascript
<!-- Deep hierarchy --> <GrandParent> <Parent> <Child> <DeepChild /> <!-- Needs data from GrandParent --> </Child> </Parent> </GrandParent>

Use provide/inject when:

  • Deep component hierarchy
  • Many intermediate components
  • Global context (theme, locale, API)

Common Mistakes

Passing non-reactive value

javascript
<!-- Wrong --> <script setup> import { provide, ref } from 'vue' const count = ref(0) provide('count', count.value) // Passes only value, not ref! </script> <!-- Correct --> <script setup> import { provide, ref } from 'vue' const count = ref(0) provide('count', count) // Passes ref </script>

Mutating data in child component

javascript
<!-- Wrong --> <script setup> import { inject } from 'vue' const theme = inject('theme') // Not recommended to mutate directly theme.value = 'dark' </script> <!-- Correct - pass function for modification --> <script setup> import { inject } from 'vue' const theme = inject('theme') const setTheme = inject('setTheme') setTheme('dark') </script>

Conclusion

provide/inject:

  • Pass data through hierarchy without props
  • Solves prop drilling problem
  • Supports reactivity
  • Can use Symbol for keys
  • Suitable for global state (theme, locale)
  • Doesn't replace Vuex/Pinia for complex state management

In interviews:

Important to be able to:

  • Explain what provide/inject is and why it's needed
  • Describe the prop drilling problem
  • Show how reactivity works with provide/inject
  • Explain difference between provide/inject and props
  • Give usage examples (theme, API client)

Content

What is provide/inject?Basic UsageComposition APIReactivityPassing Reactive DataPassing Functions for ModificationDefault ValuesSymbol KeysApplication-level provideprovide/inject vs propsWhen to use propsWhen to use provide/injectCommon MistakesPassing non-reactive valueMutating data in child componentConclusion

Short Answer

Interview ready
Premium

A concise answer to help you respond confidently on this topic during an interview.

Finished reading?
Practice Problems