JAVASCRIPT
Global State Sharing with Provide/Inject
Implement flexible dependency injection in Vue 3 using `provide` and `inject` from the Composition API for sharing data or functions across deep component hierarchies, avoiding prop drilling.
// constants.js (or any central place for keys)
export const THEME_KEY = Symbol('theme');
export const USER_INFO_KEY = Symbol('userInfo');
// App.vue (Parent component providing data)
<template>
<div :class="`app-container theme-${theme.current.value}`">
<h1>App Component (Theme: {{ theme.current.value }})</h1>
<button @click="theme.toggleTheme">Toggle Theme</button>
<UserProfile />
</div>
</template>
<script setup>
import { ref, reactive, provide } from 'vue';
import { THEME_KEY, USER_INFO_KEY } from './constants';
import UserProfile from './UserProfile.vue';
// Theme state and logic
const theme = reactive({
current: ref('light'),
toggleTheme: () => {
theme.current.value = theme.current.value === 'light' ? 'dark' : 'light';
},
});
// User info state
const userInfo = reactive({
name: 'Jane Doe',
age: 30,
email: '[email protected]',
updateName: (newName) => {
userInfo.name = newName;
},
});
// Provide these states/functions to all descendants
provide(THEME_KEY, theme);
provide(USER_INFO_KEY, userInfo);
</script>
<style>
.app-container {
font-family: Arial, sans-serif;
padding: 20px;
border-radius: 8px;
margin: 20px;
min-height: 200px;
}
.theme-light {
background-color: #f0f2f5;
color: #333;
border: 1px solid #ddd;
}
.theme-dark {
background-color: #333;
color: #f0f2f5;
border: 1px solid #555;
}
button {
padding: 8px 15px;
margin-bottom: 15px;
cursor: pointer;
border-radius: 4px;
border: none;
}
.theme-light button {
background-color: #007bff;
color: white;
}
.theme-dark button {
background-color: #6c757d;
color: white;
}
</style>
// UserProfile.vue (Intermediate component - could be many layers deep)
<template>
<div :class="`user-profile theme-${theme?.current.value}`">
<h2>User Profile</h2>
<UserDetails />
</div>
</template>
<script setup>
import { inject } from 'vue';
import { THEME_KEY } from './constants';
import UserDetails from './UserDetails.vue';
// Inject theme only to apply class, not to modify
const theme = inject(THEME_KEY);
</script>
<style scoped>
.user-profile {
padding: 15px;
border-radius: 6px;
margin-top: 20px;
}
.theme-light.user-profile {
background-color: #e9ecef;
color: #212529;
}
.theme-dark.user-profile {
background-color: #495057;
color: #f8f9fa;
}
</style>
// UserDetails.vue (Deeply nested component consuming data)
<template>
<div :class="`user-details theme-${theme?.current.value}`">
<h3>Details</h3>
<p>Name: <strong>{{ userInfo.name }}</strong></p>
<p>Age: {{ userInfo.age }}</p>
<p>Email: {{ userInfo.email }}</p>
<p>Current App Theme: {{ theme?.current.value }}</p>
<input type="text" v-model="tempName" @keyup.enter="updateUserName" />
<button @click="updateUserName">Update Name</button>
</div>
</template>
<script setup>
import { inject, ref } from 'vue';
import { THEME_KEY, USER_INFO_KEY } from './constants';
// Inject the provided data and functions
const theme = inject(THEME_KEY, { current: ref('light') }); // Provide a default in case it's not provided
const userInfo = inject(USER_INFO_KEY, { // Provide a default
name: 'Guest',
age: 'N/A',
email: 'N/A',
updateName: () => console.warn('User info service not available')
});
const tempName = ref(userInfo.name);
const updateUserName = () => {
userInfo.updateName(tempName.value);
};
</script>
<style scoped>
.user-details {
padding: 10px;
border-radius: 4px;
margin-top: 10px;
}
.theme-light.user-details {
background-color: #dee2e6;
color: #343a40;
}
.theme-dark.user-details {
background-color: #6c757d;
color: #e9ecef;
}
input {
padding: 5px;
margin-right: 10px;
border-radius: 4px;
border: 1px solid #ccc;
}
</style>
How it works: This snippet demonstrates dependency injection in Vue 3 using `provide` and `inject` from the Composition API. A parent component `provide`s reactive data (like a theme state and user information) using unique `Symbol` keys. Any descendant component, regardless of how deep, can then `inject` this data without needing to pass it down through props explicitly (prop drilling). This pattern is highly effective for sharing application-wide settings, services, or complex state across deeply nested components, making the architecture cleaner and more maintainable.