Vue Provide/Inject

Share data across component hierarchy

🔗 What is Provide/Inject?

Provide and Inject enable parent components to share data with all descendants without prop drilling. The parent provides values, and any descendant can inject them, regardless of component depth in the hierarchy.


<!-- Parent Component -->
<script setup>
import { provide, ref } from 'vue'

const theme = ref('dark')
provide('theme', theme)
</script>

<!-- Child Component (any level deep) -->
<script setup>
import { inject } from 'vue'

const theme = inject('theme')
// Now can use theme.value
</script>
                                    

Key Provide/Inject Concepts

📤

Provide

Share data from parent

provide('key', value)
📥

Inject

Receive data in descendants

const value = inject('key')
🔄

Reactivity

Provided refs stay reactive

const count = ref(0)
provide('count', count)
🛡️

Default Values

Fallback if not provided

inject('key', 'default')

🔹 Basic Provide/Inject

Share data from parent to any descendant:

<!-- App.vue (Parent) -->
<template>
  <div>
    <h1>App</h1>
    <ChildComponent />
  </div>
</template>

<script setup>
import { provide, ref } from 'vue'
import ChildComponent from './ChildComponent.vue'

const username = ref('John Doe')
const userRole = ref('admin')

// Provide values to all descendants
provide('username', username)
provide('userRole', userRole)
</script>

<!-- GrandchildComponent.vue (Deep descendant) -->
<template>
  <div>
    <p>User: {{ username }}</p>
    <p>Role: {{ userRole }}</p>
  </div>
</template>

<script setup>
import { inject } from 'vue'

// Inject values from ancestor
const username = inject('username')
const userRole = inject('userRole')
</script>

🔹 Reactive Provide/Inject

Provided reactive values update across all components:

<!-- Parent Component -->
<template>
  <div>
    <button @click="count++">Increment: {{ count }}</button>
    <ChildComponent />
  </div>
</template>

<script setup>
import { provide, ref } from 'vue'
import ChildComponent from './ChildComponent.vue'

const count = ref(0)

// Provide reactive ref
provide('count', count)
</script>

<!-- Child Component -->
<template>
  <div>
    <p>Count in child: {{ count }}</p>
  </div>
</template>

<script setup>
import { inject } from 'vue'

// Inject reactive value
const count = inject('count')
// count.value updates automatically!
</script>

🔹 Provide Functions

Share methods to modify provided data:

<!-- Parent Component -->
<template>
  <div>
    <h2>Theme: {{ theme }}</h2>
    <ChildComponent />
  </div>
</template>

<script setup>
import { provide, ref } from 'vue'
import ChildComponent from './ChildComponent.vue'

const theme = ref('light')

function toggleTheme() {
  theme.value = theme.value === 'light' ? 'dark' : 'light'
}

// Provide both data and function
provide('theme', theme)
provide('toggleTheme', toggleTheme)
</script>

<!-- Child Component -->
<template>
  <div>
    <p>Current theme: {{ theme }}</p>
    <button @click="toggleTheme">Toggle Theme</button>
  </div>
</template>

<script setup>
import { inject } from 'vue'

const theme = inject('theme')
const toggleTheme = inject('toggleTheme')
</script>

🔹 Default Values

Provide fallback values when injection might fail:

<template>
  <div>
    <p>User: {{ username }}</p>
    <p>Theme: {{ theme }}</p>
    <p>Language: {{ language }}</p>
  </div>
</template>

<script setup>
import { inject } from 'vue'

// Inject with default values
const username = inject('username', 'Guest')
const theme = inject('theme', 'light')
const language = inject('language', 'en')

// If parent doesn't provide these values,
// defaults will be used
</script>

🔹 Provide Object Pattern

Group related data and methods together:

<!-- Parent Component -->
<script setup>
import { provide, ref, readonly } from 'vue'

const user = ref({
  name: 'Alice',
  email: '[email protected]',
  role: 'admin'
})

function updateUser(updates) {
  user.value = { ...user.value, ...updates }
}

// Provide as object
provide('userStore', {
  user: readonly(user), // Make read-only
  updateUser
})
</script>

<!-- Child Component -->
<template>
  <div>
    <p>{{ userStore.user.name }}</p>
    <button @click="changeName">Change Name</button>
  </div>
</template>

<script setup>
import { inject } from 'vue'

const userStore = inject('userStore')

function changeName() {
  userStore.updateUser({ name: 'Bob' })
}
</script>

🔹 App-Level Provide

Provide values to entire application:

// main.js
import { createApp } from 'vue'
import App from './App.vue'

const app = createApp(App)

// Provide at app level
app.provide('apiUrl', 'https://api.example.com')
app.provide('appVersion', '1.0.0')

app.mount('#app')

// Now any component can inject these values
// const apiUrl = inject('apiUrl')
// const appVersion = inject('appVersion')

🧠 Test Your Knowledge

What is the main benefit of Provide/Inject?