0
# Dependency Injection
1
2
Vue's dependency injection system enables sharing data across component hierarchies without prop drilling. It provides type-safe injection with optional default values and context checking.
3
4
## Capabilities
5
6
### Provide & Inject Functions
7
8
Share data from parent components to descendant components at any depth.
9
10
```typescript { .api }
11
/**
12
* Provides a value that can be injected by descendant components
13
* @param key - Injection key (string or symbol)
14
* @param value - Value to provide
15
*/
16
function provide<T>(key: InjectionKey<T> | string, value: T): void;
17
18
/**
19
* Injects a value provided by an ancestor component
20
* @param key - Injection key to look for
21
* @returns Injected value or undefined if not found
22
*/
23
function inject<T>(key: InjectionKey<T> | string): T | undefined;
24
25
/**
26
* Injects a value with a default fallback
27
* @param key - Injection key to look for
28
* @param defaultValue - Default value if injection not found
29
* @returns Injected value or default value
30
*/
31
function inject<T>(key: InjectionKey<T> | string, defaultValue: T): T;
32
33
/**
34
* Injects a value with a factory function for default value
35
* @param key - Injection key to look for
36
* @param defaultValue - Factory function or default value
37
* @param treatAsFactory - Whether to treat defaultValue as factory
38
* @returns Injected value or result of default factory
39
*/
40
function inject<T>(
41
key: InjectionKey<T> | string,
42
defaultValue: T | (() => T),
43
treatAsFactory: boolean
44
): T;
45
46
/**
47
* Checks if injection context is available (component is in setup)
48
* @returns True if injection context is available
49
*/
50
function hasInjectionContext(): boolean;
51
```
52
53
**Usage Examples:**
54
55
```typescript
56
import { defineComponent, ref, provide, inject, InjectionKey } from "@vue/runtime-core";
57
58
// Define typed injection keys
59
const ThemeKey: InjectionKey<{ theme: string; toggleTheme: () => void }> = Symbol('theme');
60
const UserKey: InjectionKey<{ name: string; id: number }> = Symbol('user');
61
62
// Parent component providing values
63
const ParentComponent = defineComponent({
64
setup() {
65
const theme = ref('light');
66
67
const toggleTheme = () => {
68
theme.value = theme.value === 'light' ? 'dark' : 'light';
69
};
70
71
const user = { name: 'Alice', id: 123 };
72
73
// Provide values with typed keys
74
provide(ThemeKey, {
75
theme: theme.value,
76
toggleTheme
77
});
78
79
provide(UserKey, user);
80
81
// Provide with string key
82
provide('api-base-url', 'https://api.example.com');
83
84
return { theme };
85
}
86
});
87
88
// Child component injecting values
89
const ChildComponent = defineComponent({
90
setup() {
91
// Inject with typed key
92
const themeContext = inject(ThemeKey);
93
if (themeContext) {
94
console.log('Current theme:', themeContext.theme);
95
}
96
97
// Inject with default value
98
const user = inject(UserKey, { name: 'Guest', id: 0 });
99
console.log('User:', user.name);
100
101
// Inject string key with default
102
const apiUrl = inject('api-base-url', 'https://fallback.com');
103
104
// Inject with factory default
105
const config = inject('app-config', () => ({ debug: false }), true);
106
107
return {
108
themeContext,
109
user,
110
apiUrl,
111
config
112
};
113
}
114
});
115
```
116
117
### Injection Context Checking
118
119
Verify if injection context is available before attempting injection.
120
121
```typescript { .api }
122
/**
123
* Checks if injection context is available
124
* @returns True if currently in component setup context
125
*/
126
function hasInjectionContext(): boolean;
127
```
128
129
**Usage Examples:**
130
131
```typescript
132
import { inject, hasInjectionContext } from "@vue/runtime-core";
133
134
// Utility function that can be called from setup or outside
135
function useTheme() {
136
if (hasInjectionContext()) {
137
// We're in a component setup context
138
const theme = inject('theme', 'light');
139
return { theme };
140
} else {
141
// We're outside component context, return default
142
console.warn('useTheme called outside component context');
143
return { theme: 'light' };
144
}
145
}
146
147
const MyComponent = defineComponent({
148
setup() {
149
// This will work - we're in setup context
150
const { theme } = useTheme();
151
152
return { theme };
153
}
154
});
155
156
// This will return default - not in component context
157
const { theme } = useTheme();
158
```
159
160
### Advanced Injection Patterns
161
162
Common patterns for complex injection scenarios.
163
164
```typescript
165
import { defineComponent, provide, inject, ref, computed, InjectionKey } from "@vue/runtime-core";
166
167
// Store-like pattern
168
interface Store {
169
state: { count: number };
170
getters: { doubledCount: number };
171
actions: { increment(): void; decrement(): void };
172
}
173
174
const StoreKey: InjectionKey<Store> = Symbol('store');
175
176
const StoreProvider = defineComponent({
177
setup(_, { slots }) {
178
const count = ref(0);
179
180
const store: Store = {
181
state: { count: count.value },
182
getters: {
183
get doubledCount() { return count.value * 2; }
184
},
185
actions: {
186
increment: () => count.value++,
187
decrement: () => count.value--
188
}
189
};
190
191
provide(StoreKey, store);
192
193
return () => slots.default?.();
194
}
195
});
196
197
// Configuration provider pattern
198
interface AppConfig {
199
apiUrl: string;
200
features: { darkMode: boolean; analytics: boolean };
201
version: string;
202
}
203
204
const ConfigKey: InjectionKey<AppConfig> = Symbol('config');
205
206
const ConfigProvider = defineComponent({
207
props: {
208
config: { type: Object as PropType<AppConfig>, required: true }
209
},
210
setup(props, { slots }) {
211
provide(ConfigKey, props.config);
212
return () => slots.default?.();
213
}
214
});
215
216
// Hook pattern for consuming injected values
217
function useStore() {
218
const store = inject(StoreKey);
219
if (!store) {
220
throw new Error('useStore must be used within StoreProvider');
221
}
222
return store;
223
}
224
225
function useConfig() {
226
const config = inject(ConfigKey);
227
if (!config) {
228
throw new Error('useConfig must be used within ConfigProvider');
229
}
230
return config;
231
}
232
```
233
234
## Types
235
236
```typescript { .api }
237
interface InjectionKey<T> extends Symbol {
238
readonly [InjectionKeySymbol]: T;
239
}
240
241
type InjectionConstraint<T> = InjectionKey<T> | string;
242
243
// Helper type for creating injection keys
244
function createInjectionKey<T>(description?: string): InjectionKey<T>;
245
```
246
247
## Best Practices
248
249
### Type Safety
250
251
Use `InjectionKey<T>` for type-safe injection:
252
253
```typescript
254
// Good: Type-safe injection key
255
const UserKey: InjectionKey<User> = Symbol('user');
256
provide(UserKey, user);
257
const injectedUser = inject(UserKey); // Type is User | undefined
258
259
// Avoid: String keys lose type information
260
provide('user', user);
261
const injectedUser = inject('user'); // Type is unknown
262
```
263
264
### Error Handling
265
266
Always handle cases where injection might fail:
267
268
```typescript
269
// Good: Handle missing injection
270
const user = inject(UserKey);
271
if (!user) {
272
throw new Error('User not provided');
273
}
274
275
// Good: Provide default value
276
const user = inject(UserKey, { name: 'Guest', id: 0 });
277
278
// Good: Create helper hook
279
function useUser() {
280
const user = inject(UserKey);
281
if (!user) {
282
throw new Error('useUser must be used within UserProvider');
283
}
284
return user;
285
}
286
```
287
288
### Scoped Providers
289
290
Create provider components for clean injection boundaries:
291
292
```typescript
293
const ThemeProvider = defineComponent({
294
setup(_, { slots }) {
295
const theme = ref('light');
296
provide(ThemeKey, { theme, setTheme: (t: string) => theme.value = t });
297
return () => slots.default?.();
298
}
299
});
300
```