0
# Dependency Injection
1
2
Provide/inject system for passing data down the component tree without explicit prop drilling. This enables ancestor components to serve as dependency providers for all their descendants, regardless of how deep the component hierarchy is.
3
4
## Capabilities
5
6
### Provide Function
7
8
Provides a value that can be injected by descendant components. Values are provided with a key and can be accessed by any descendant component.
9
10
```typescript { .api }
11
/**
12
* Provides a value for descendant components to inject
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
interface InjectionKey<T> extends Symbol {}
19
```
20
21
**Usage Examples:**
22
23
```typescript
24
import { provide, reactive } from "@vue/composition-api";
25
26
// String-based key
27
export default {
28
setup() {
29
const theme = reactive({
30
primaryColor: "#007bff",
31
secondaryColor: "#6c757d",
32
darkMode: false,
33
});
34
35
provide("theme", theme);
36
37
const userSettings = reactive({
38
language: "en",
39
timezone: "UTC",
40
});
41
42
provide("userSettings", userSettings);
43
44
return {};
45
},
46
};
47
48
// Symbol-based key for type safety
49
import { InjectionKey } from "@vue/composition-api";
50
51
interface Theme {
52
primaryColor: string;
53
secondaryColor: string;
54
darkMode: boolean;
55
}
56
57
const ThemeKey: InjectionKey<Theme> = Symbol("theme");
58
59
export default {
60
setup() {
61
const theme = reactive({
62
primaryColor: "#007bff",
63
secondaryColor: "#6c757d",
64
darkMode: false,
65
});
66
67
provide(ThemeKey, theme);
68
69
return {};
70
},
71
};
72
```
73
74
### Inject Function
75
76
Injects a value provided by an ancestor component. Supports default values and type-safe injection keys.
77
78
```typescript { .api }
79
/**
80
* Injects a value provided by an ancestor component
81
* @param key - Injection key to look for
82
* @returns Injected value or undefined if not found
83
*/
84
function inject<T>(key: InjectionKey<T> | string): T | undefined;
85
86
/**
87
* Injects a value with a default fallback
88
* @param key - Injection key to look for
89
* @param defaultValue - Default value if injection key not found
90
* @returns Injected value or default value
91
*/
92
function inject<T>(key: InjectionKey<T> | string, defaultValue: T): T;
93
94
/**
95
* Injects a value with a default factory function
96
* @param key - Injection key to look for
97
* @param defaultValue - Factory function for default value
98
* @param treatDefaultAsFactory - Whether to treat default as factory (true by default)
99
* @returns Injected value or result of default factory
100
*/
101
function inject<T>(
102
key: InjectionKey<T> | string,
103
defaultValue: () => T,
104
treatDefaultAsFactory: boolean
105
): T;
106
```
107
108
**Usage Examples:**
109
110
```typescript
111
import { inject } from "@vue/composition-api";
112
113
// Basic injection
114
export default {
115
setup() {
116
const theme = inject("theme");
117
118
if (theme) {
119
console.log("Current theme:", theme);
120
}
121
122
return {
123
theme,
124
};
125
},
126
};
127
128
// Injection with default value
129
export default {
130
setup() {
131
const theme = inject("theme", {
132
primaryColor: "#000000",
133
secondaryColor: "#ffffff",
134
darkMode: false,
135
});
136
137
return {
138
theme,
139
};
140
},
141
};
142
143
// Type-safe injection with symbol key
144
import { InjectionKey, inject } from "@vue/composition-api";
145
146
interface Theme {
147
primaryColor: string;
148
secondaryColor: string;
149
darkMode: boolean;
150
}
151
152
const ThemeKey: InjectionKey<Theme> = Symbol("theme");
153
154
export default {
155
setup() {
156
const theme = inject(ThemeKey); // TypeScript knows this is Theme | undefined
157
const safeTheme = inject(ThemeKey, {
158
primaryColor: "#000000",
159
secondaryColor: "#ffffff",
160
darkMode: false,
161
}); // TypeScript knows this is Theme
162
163
return {
164
theme,
165
safeTheme,
166
};
167
},
168
};
169
```
170
171
### Advanced Injection Patterns
172
173
Complex patterns for dependency injection including reactive providers, service injection, and contextual data.
174
175
**Reactive Provider Pattern:**
176
177
```typescript
178
import { provide, reactive, readonly } from "@vue/composition-api";
179
180
// Provider component
181
export default {
182
setup() {
183
const state = reactive({
184
user: null,
185
isAuthenticated: false,
186
permissions: [],
187
});
188
189
const actions = {
190
login: async (credentials: any) => {
191
// Login logic
192
state.user = await authenticate(credentials);
193
state.isAuthenticated = true;
194
state.permissions = await getUserPermissions(state.user.id);
195
},
196
logout: () => {
197
state.user = null;
198
state.isAuthenticated = false;
199
state.permissions = [];
200
},
201
};
202
203
// Provide readonly state and actions
204
provide("auth", {
205
state: readonly(state),
206
actions,
207
});
208
209
return {};
210
},
211
};
212
213
// Consumer component
214
import { inject, computed } from "@vue/composition-api";
215
216
export default {
217
setup() {
218
const auth = inject("auth");
219
220
const canEditPosts = computed(() => {
221
return auth?.state.permissions.includes("edit:posts") ?? false;
222
});
223
224
const handleLogin = async () => {
225
await auth?.actions.login({ username: "user", password: "pass" });
226
};
227
228
return {
229
auth,
230
canEditPosts,
231
handleLogin,
232
};
233
},
234
};
235
```
236
237
**Service Injection Pattern:**
238
239
```typescript { .api }
240
import { provide, inject, InjectionKey } from "@vue/composition-api";
241
242
// Service interface
243
interface ApiService {
244
get<T>(url: string): Promise<T>;
245
post<T>(url: string, data: any): Promise<T>;
246
put<T>(url: string, data: any): Promise<T>;
247
delete(url: string): Promise<void>;
248
}
249
250
// Service implementation
251
class HttpApiService implements ApiService {
252
private baseUrl: string;
253
254
constructor(baseUrl: string) {
255
this.baseUrl = baseUrl;
256
}
257
258
async get<T>(url: string): Promise<T> {
259
const response = await fetch(`${this.baseUrl}${url}`);
260
return response.json();
261
}
262
263
async post<T>(url: string, data: any): Promise<T> {
264
const response = await fetch(`${this.baseUrl}${url}`, {
265
method: "POST",
266
headers: { "Content-Type": "application/json" },
267
body: JSON.stringify(data),
268
});
269
return response.json();
270
}
271
272
async put<T>(url: string, data: any): Promise<T> {
273
const response = await fetch(`${this.baseUrl}${url}`, {
274
method: "PUT",
275
headers: { "Content-Type": "application/json" },
276
body: JSON.stringify(data),
277
});
278
return response.json();
279
}
280
281
async delete(url: string): Promise<void> {
282
await fetch(`${this.baseUrl}${url}`, { method: "DELETE" });
283
}
284
}
285
286
// Service key
287
const ApiServiceKey: InjectionKey<ApiService> = Symbol("apiService");
288
289
// Root component provides service
290
export default {
291
setup() {
292
const apiService = new HttpApiService("/api");
293
provide(ApiServiceKey, apiService);
294
295
return {};
296
},
297
};
298
299
// Component uses service
300
export default {
301
setup() {
302
const api = inject(ApiServiceKey);
303
304
const loadData = async () => {
305
if (api) {
306
const data = await api.get("/users");
307
console.log("Users:", data);
308
}
309
};
310
311
return {
312
loadData,
313
};
314
},
315
};
316
```
317
318
**Configuration Injection:**
319
320
```typescript { .api }
321
import { provide, inject, InjectionKey } from "@vue/composition-api";
322
323
interface AppConfig {
324
apiUrl: string;
325
version: string;
326
features: {
327
darkMode: boolean;
328
notifications: boolean;
329
analytics: boolean;
330
};
331
}
332
333
const ConfigKey: InjectionKey<AppConfig> = Symbol("config");
334
335
// App root provides configuration
336
export default {
337
setup() {
338
const config: AppConfig = {
339
apiUrl: process.env.VUE_APP_API_URL || "/api",
340
version: process.env.VUE_APP_VERSION || "1.0.0",
341
features: {
342
darkMode: true,
343
notifications: true,
344
analytics: process.env.NODE_ENV === "production",
345
},
346
};
347
348
provide(ConfigKey, config);
349
350
return {};
351
},
352
};
353
354
// Component uses configuration
355
export default {
356
setup() {
357
const config = inject(ConfigKey, {
358
apiUrl: "/api",
359
version: "unknown",
360
features: { darkMode: false, notifications: false, analytics: false },
361
});
362
363
return {
364
config,
365
};
366
},
367
};
368
```
369
370
### Nested Providers
371
372
Providers can override values at different levels of the component tree, allowing for contextual customization.
373
374
```typescript
375
import { provide, inject } from "@vue/composition-api";
376
377
// Root level provider
378
export const RootProvider = {
379
setup() {
380
provide("theme", "light");
381
provide("lang", "en");
382
383
return {};
384
},
385
};
386
387
// Section level provider (overrides theme)
388
export const DarkSection = {
389
setup() {
390
provide("theme", "dark"); // Overrides root theme
391
// lang is still "en" from root
392
393
return {};
394
},
395
};
396
397
// Component uses nearest provider
398
export const ThemedComponent = {
399
setup() {
400
const theme = inject("theme", "light"); // Gets "dark" from DarkSection
401
const lang = inject("lang", "en"); // Gets "en" from root
402
403
return {
404
theme,
405
lang,
406
};
407
},
408
};
409
```
410
411
### Injection with Composables
412
413
Creating reusable composables that encapsulate injection logic.
414
415
```typescript { .api }
416
import { inject, InjectionKey, computed } from "@vue/composition-api";
417
418
interface User {
419
id: string;
420
name: string;
421
email: string;
422
role: string;
423
}
424
425
const UserKey: InjectionKey<User | null> = Symbol("user");
426
427
// Composable for user injection
428
export function useUser() {
429
const user = inject(UserKey, null);
430
431
const isLoggedIn = computed(() => user !== null);
432
const isAdmin = computed(() => user?.role === "admin" ?? false);
433
const canEdit = computed(() => isAdmin.value || user?.role === "editor");
434
435
return {
436
user,
437
isLoggedIn,
438
isAdmin,
439
canEdit,
440
};
441
}
442
443
// Usage in component
444
export default {
445
setup() {
446
const { user, isLoggedIn, isAdmin, canEdit } = useUser();
447
448
return {
449
user,
450
isLoggedIn,
451
isAdmin,
452
canEdit,
453
};
454
},
455
};
456
```
457
458
## Types
459
460
```typescript { .api }
461
interface InjectionKey<T> extends Symbol {}
462
463
type InjectionConstraint = string | number | boolean | object | symbol;
464
465
interface ProvideOptions {
466
[key: string | symbol]: any;
467
}
468
469
interface InjectOptions {
470
from?: string | symbol;
471
default?: any;
472
}
473
474
type InjectKey = string | symbol;
475
```