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.

Need help integrating this into your project?

Our team of expert developers can help you build your custom application from scratch.

Hire DigitalCodeLabs