Best way to use Provide/Inject in your Vue.js project.

John Ayilara
4 min readJan 31, 2024

--

There are some features in vue.js framework that are useful and powerful but not frequently used and Provide/Inject is one of them. There are some scenarios whereby you want to pass prop(s) to a grand-component or emit a data or method to the top-level component, this can be done with props but making use of provide can be a good appraoch as well.

For an easy follow, I will make use of a familiar example to demonstrate how Provide/Inject method works 🤓.

Example:

Let’s say I have this Component structure app.vue > ListPage.vue > List.vue.

File Structure

… and I have a list in the app.vue I need to pass down to List.vue thus I can use props as expected right?

App.vue

<script>
import ListPage from "./components/ListPage.vue";
export default {
name: "App",
components: {
ListPage,
},
data() {
return {
list: [
{
id: 1,
name: "Item 1",
},
{
id: 2,
name: "Item 2",
},
{
id: 3,
name: "Item 3",
},
{
id: 4,
name: "Item 4",
},
],
};
},
};
</script>

<template>
<div>
<list-page :list="list" />
</div>
</template>
ListPage.vue

<script>
import List from "./List.vue";
export default {
name: "ListPage",
components: {
List,
},
props: {
list: {
type: Array,
},
},
};
</script>

<template>
<div>
<h1>List Page</h1>
<list :list="list" />
</div>
</template>
List.vue

<script>
export default {
name: "List",
props: {
list: {
type: Array,
},
},
};
</script>

<template>
<div>
<ul>
<li v-for="item in list" :key="item.id">
<p>
{{ item.name }}
</p>
<button>delete</button>
</li>
</ul>
</div>
</template>

Now, our list items have a delete button which means we need to emit a function to delete the particular item using its unique id property.

The button tag in List.vue changed to

<button @click="deleteItem(item.id)">delete</button>

and these were added to the List.vue script.

emits: ["delete"],
methods: {
deleteItem(id) {
this.$emit("delete", id);
},
},

On the ListPage Component, the emit is handled thus the list tag changed to:

<list @delete="deleteItem" :list="list" />

and these were added to the script section on ListPage component.

  emits: ["deleteItem"],
methods: {
deleteItem(id) {
this.$emit("deleteItem", id);
},
},

And finally, On the App.vue component, the emit is being handled and the ListPage tag is changed to:

<list-page @deleteItem="handleDeleteItem" :list="list" />

Also, this method is added to the App.vue

methods: {
handleDeleteItem(id) {
this.list = this.list.filter((item) => item.id !== id);
},
},

This method works perfectly and probably most used method, but in some case (complex application) whereby you have to pass data down to 4th or 6th level component, it can be a lot of stress passing props and emitting data/methods through these components😮‍💨. This could cause multiple components updating on a single action needed in just one component. However, Provide/Inject can come to our rescue in this case.

Provide/Inject Method

Pass the data through the provide method. To read more about reactive provide and non-reactive provide click here. But in this example, we are going to make use of the reactive provide because of the delete method thus we need the list data being passed to be reactive.

First, reset all the changes we made for the props and emits methods across the components because we won’t be needing it anymore 🏌️‍♂️.

Then on your App.vue add the provide method in your script just like below:

<script>
import ListPage from "./components/ListPage.vue";
export default {
name: "App",
components: {
ListPage,
},
data() {
return {
list: [...
],
};
},
provide() {
return {
list: this.list,
};
},
};
</script>

<template>
<div>
<list-page />
</div>
</template>

And on the List.vue component, add the inject (just like props) in your script to have this:

<script>
export default {
name: "List",
inject: ["list"],
};
</script>

<template>
<div>
<ul>
<li v-for="item in list" :key="item.id">
<p>
{{ item.name }}
</p>
<button>delete</button>
</li>
</ul>
</div>
</template>

The list still shows as before without the props, but we are making use of the power of Provide/Inject.

Now we need to add the delete feature to the button. To do this. we can just provide the deleteItem method from App.vue and inject it in the List.vue component, then the deleteItem will be called once invoked in the injected component.

  1. Add your deleteItem method back.
 methods: {
deleteItem(id) {
const itemIndex = this.list.findIndex((item) => item.id === id); // For DOM reactiveness, this array method option has to be used
this.list.splice(itemIndex, 1);
},
},

2. Provide this function

  provide() {
return {
list: this.list,
deleteItem: this.deleteItem,
};
},

3. In your List.vue component, Inject the deleteItem method.

inject: ["list", "deleteItem"],

4. Invoke the deleteItem method injected on “@click” method

<button @click="deleteItem(item.id)">delete</button>

And the code still works same way with the props/emits method but with fewer code.

Conclusion, Provide/Inject method is rarely used in projects mostly because it can be difficult to track where the provide data or method is being used. However, for a personal project, I think it’s a fun and good to do tool to use if you don’t like the idea of using store for this.

--

--