Dependency injection is a great pattern to use while building large and complex applications. The major challenge with building these applications is creating loosely coupled components, and this is where dependency management is most critical.
This article will introduce dependency injection, its pros and cons, and how dependency injection can be handled in Vue projects.
Dependency injection is a design pattern in which classes are not allowed to create dependencies. Rather, they request dependencies from external sources. This design pattern strongly holds that a class should not configure its dependencies statically.
Why should we use dependency injection in Vue when we can pass data from parent components down to the children components?
Some experience with using props would expose you to the term prop drilling, which is the process where props are passed from one part of the component tree to another by going through other parts that do not need the data, but only help in passing it through the tree:
RexComponent (Anyone needs my wallet address?) ├── TomComponent ├── PeterComponent ├── DurryComponent (yes I need it)
With the above snippet, let’s consider a scenario where RexComponent
has a wallet address to give out and DurryComponent
is the only one in need of the wallet address. We will have to pass the wallet address from RexComponent
to TomComponent
to PeterComponent
, and finally to DurryComponent
. This results in the redundant piece of code in both TomComponent
and PeterComponent
.
With dependency injection, DurryComponent
would receive the wallet from RexComponent
without passing through TomComponent
and PeterComponent
.
To handle dependency injection in Vue, the provide and inject options are provided out of the box.
The dependencies to be injected is made available by the parent component using the provide property as follows:
//Parent component <script lang="ts"> import {Component, Vue} from 'vue-property-decorator'; import Child from '@/components/Child.vue'; @Component({ components: { Child }, provide: { 'name': 'Paul', }, }) export default class Parent extends Vue { } </script>
The provided dependency is injected into the child component using the injected property:
<template> <h1> My name is {{name}}</h1> </template> <script lang="ts"> import {Component, Inject, Vue} from 'vue-property-decorator'; @Component({}) export default class Child extends Vue { @Inject('name') name!: string; // non-null assertion operator } </script>
The vue-property-decorator
also exposes @Provide
decorator for declaring providers.
Using the @Provide
decorator, we can make dependencies available in the parent component:
//Parent component export default class ParentComponent extends Vue { @Provide("user-details") userDetails: { name: string } = { name: "Paul" }; }
Similarly, dependencies can be injected into the child component:
//Child component <script lang="ts"> import {Component, Inject, Vue} from 'vue-property-decorator'; @Component({}) export default class ChildComponent extends Vue { @Inject('user-details') user!: { name: string }; } </script>
The provider hierarchy rule states that if the same provider key is used in multiple providers in the dependency tree of a component, then the provider of the closest parent to the child component will override other providers higher in the hierarchy.
Let’s consider the following snippet for ease of understanding:
FatherComponent ├── SonComponent ├── GrandsonComponent //Father component <script lang="ts"> import {Component, Vue} from 'vue-property-decorator'; import SonComponent from '@/components/Son.vue'; @Component({ components: { SonComponent }, provide: { 'family-name': 'De Ekongs', }, }) export default class FatherComponent extends Vue { } </script>
In the above snippet, the family-name
dependency is provided by the FatherComponent
:
//Son component <script lang="ts"> import {Component, Vue} from 'vue-property-decorator'; import GrandsonComponent from '@/components/Grandson.vue'; @Component({ components: { GrandsonComponent }, provide: { 'family-name': 'De Royals', }, }) export default class SonComponent extends Vue { } </script>
In the above snippet, the SonComponent
overrides the family-name
dependency previously provided by the FatherComponent
:
//Grand son Component <template> <h1> Our family name is {{familyName}}</h1> </template> <script lang="ts"> import {Component, Inject, Vue} from 'vue-property-decorator'; @Component({}) export default class Child extends Vue { @Inject('family-name') familyName!: string; // non-null assertion operator } </script>
As you would guess, De Royals
will be rendered in the template of the GrandsonComponent
.
In some complex Vue projects, you might avoid overriding dependencies to achieve consistency in the codebase. In such situations, overriding dependencies is seen as a limitation.
Fortunately, JavaScript has provided us with the ES6 symbols as a remedy to the drawback associated with multiple providers with the same keys.
According to MDN, “Symbols are often used to add unique property keys to an object that won’t collide with keys any other code might add to the object, and which are hidden from any mechanisms other code will typically use to access the object.”
In other words, every symbol has a unique identity:
Symbol('foo') === Symbol('foo') // false
Instead of using the same string key on the provider and injection sides as we did in our previous code, we can use the ES6 Symbol
. This will ensure that no dependency gets overridden by another:
export const FAMILY = { FAMILY_NAME: Symbol('FAMILYNAME'), };
In this article, we established a basic understanding of dependency injection in Vue. We walked through the drawbacks associated with multiple providers with the same keys while we also implemented a remedy to the drawback using the ES6 symbols.
Debugging Vue.js applications can be difficult, especially when users experience issues that are difficult to reproduce. If you’re interested in monitoring and tracking Vue mutations and actions for all of your users in production, try LogRocket.
LogRocket lets you replay user sessions, eliminating guesswork by showing exactly what users experienced. It captures console logs, errors, network requests, and pixel-perfect DOM recordings — compatible with all frameworks.
With Galileo AI, you can instantly identify and explain user struggles with automated monitoring of your entire product experience.
Modernize how you debug your Vue apps — start monitoring for free.
Would you be interested in joining LogRocket's developer community?
Join LogRocket’s Content Advisory Board. You’ll help inform the type of content we create and get access to exclusive meetups, social accreditation, and swag.
Sign up nowLearn how React’s new use() API elevates state management and async data fetching for modern, efficient components.
Next.js 15 caching overhaul: Fix overcaching with Dynamic IO and the use cache directive.
LLM QA isn’t just a tooling gap — it’s a fundamental shift in how we think about software reliability.
Windsurf AI brings agentic coding and terminal control right into your IDE. We compare it to Cursor, explore its features, and build a real frontend project.
6 Replies to "Dependency injection in Vue: Advantages and caveats"
Hey! Vue provide variables can be reactive with vue3 composition api. The ref method activate this ability.
Thanks Ismail.
Why would you need to inject dependencies when you have Vuex? The prop drilling example is easily mitigated with state management.
While Vuex can mitigate prop drilling, it is worth noting that it can be an overkill and added complexity for small to medium Vuejs applications.
With dependency injection, DurryComponent would receive the wallet from RexComponent without passing through TomComponent and PeterComponent.
Actually no. The dependency will traverse the entire descendant tree and will be available to any nested components. That’s the main problem with vue di. You can’t control the di process.
Hi Predi, actually that illustration was a quick comparison between dependency injection and prop drilling. With DI we don’t have to pass the values manually like we do with props. I will provide some updates for more clarity. Thanks!