Usually, when we want to pass data from a parent to a child component, we use props. Vue.js has made this straightforward and easy to do. But we’ve probably experienced frustration at some point when we need to pass data from a parent-level component to a deeply nested child component.
If we were to use props, we would end up passing the data to each component on each level of the Vue component tree for the data to get to its final destination. This is called prop drilling and could cause our app to look more complex than it is. And if it were an application with a simple state, using Vuex in it would be overkill.
Luckily for us, Vue has the provide
/inject
API, and with the introduction of the Composition API in Vue 3, it has never been better.
Using the provide
and inject
pair, parent components can send data to their children components regardless of how deep the component hierarchy is. The parent component has a provide
function to supply data, and the child component has an inject
function to start using this data.
In the image above, we have three levels of children components. The data we want to pass is contained in the parent component, and the desired destination for the data is deeply nested in the third level of the component tree. We could achieve this using props, but at the expense of our code’s simplicity and readability. Let’s see how we can do this without sacrificing either.
First, we need to install the latest version of Vue CLI v4.5 with the command below:
yarn global add @vue/cli@next#ORnpm install -g @vue/cli@next
The run the command below to create a new Vue app:
vue create provide-inject-tutorial
Using the provide
API
The provide
API is a function we use to define the data we want to be passed down to a child component.
When using the provide
function in setup()
, we start by explicitly importing the function from vue
. This allows us to define each property when provide
is called.
The provide
function accepts two parameters:
- The property’s name (a string)
- The property’s value (a string or an object containing multiple values)
<!-- src/components/MyMap.vue --><template> <MyMarker /></template><script>import { provide } from 'vue'import MyMarker from './MyMarker.vueexport default { components: { MyMarker }, setup() { provide('location', 'North Pole') provide('geolocation', { longitude: 90, latitude: 135 }) }}</script>
After we’ve imported the provide
function in the code above, we invoke it inside the setup
function. Next, we pass the parameters for the first provide
function as follows: the property name 'location'
and a single value, 'North Pole'
.
For the second provide
function, we pass an object containing the latitude
and longitude
values and set its property name as 'geolocation'
.
Using the inject
API
The inject
API is a function we use to receive data from our provider component.
As we did with the provide
function, we also have to import the inject
function from vue
. This lets us call and use the function anywhere in our component.
The inject
function takes two parameters:
- The name of the property being injected
- An optional default value
Let’s have a look at the code below:
<!-- src/components/MyMarker.vue --><script>import { inject } from 'vue'export default { setup() { const userLocation = inject('location', 'The Universe') const userGeolocation = inject('geolocation') return { userLocation, userGeolocation } }}</script>
First we import the inject
function into our MyMarker
component. Then, inside our setup
function, we assign the first provide
function with a property name 'location'
to the userLocation
variable. We also provide an optional default fallback value, 'The Universe'
.
Next, we assign the second provide
function with a property name of 'geolocation'
to the userGeoLocation
variable. We return
both the userLocation
and userGeoLocation
variables, after which we are free to use their values anywhere in the MyMarker
component.
Making the provide
/inject
pair reactive
Sadly, straight out the box, the provide
/inject
pair is not reactive. Thankfully, there is a way to go about making this happen by using either the ref
or reactive
function provided by the Vue API.
We first have to import them from vue
, then we invoke the ref
or reactive
function. We’ll set its parameters to be the value(s) we want to pass to the desired child component and store the function in a variable. We then invoke the provide
function and pass the property name and its value.
Now, if anything changes in either property, the MyMarker
component will automatically be updated as well!
We can now update our code as follows:
<!-- src/components/MyMap.vue --><template> <MyMarker /></template><script>import { provide, reactive, ref } from 'vue'import MyMarker from './MyMarker.vueexport default { components: { MyMarker }, setup() { const location = ref('North Pole') const geolocation = reactive({ longitude: 90, latitude: 135 }) provide('location', location) provide('geolocation', geolocation) }}</script>
After importing the ref
and reactive
functions, we invoke the ref
function and give it a parameter (the value 'North Pole'
) and then assign the ref
function to the location
variable.
For the reactive
function, we invoke it and pass it a parameter in the form of an object. Then we assign the reactive
function to the geolocation
variable. After we’ve done this, we can call the provide
function and pass it the property name and the value of the data we want to pass down.
In the first provide
function, we set the property name to 'location'
and set its value equal to location
, which is the value we assigned to the ref
function.
While in the second provide
function, we set its property name to 'geolocation'
and its value equal to geolocation
, which is the value we assigned to the reactive
function.
When to use the provide
/inject
function pair
- If the app has a fairly simple state and using Vuex would be overkill
- If your application has too many component levels, and the components in between don’t use the data before it’s passed to the desired component
- If the data is only used by a few components. But if the data will be used by many more components, Vuex would be a better solution
We’ve learned how to use the provide
/inject
function pair to pass data between deeply nested components in Vue.js 3 with the Composition API. We’ve covered how to make it reactive and also the different use cases for when you should use it. To learn more about the provide
/inject
function pair, visit the official documentation.
Experience your Vue apps exactly how a user does
Debugging Vue.js applications can be difficult, especially when there are dozens, if not hundreds of mutations during a user session. If you’re interested in monitoring and tracking Vue mutations for all of your users in production, try LogRocket.
LogRocket is like a DVR for web and mobile apps, recording literally everything that happens in your Vue apps, including network requests, JavaScript errors, performance problems, and much more. Instead of guessing why problems happen, you can aggregate and report on what state your application was in when an issue occurred.
The LogRocket Vuex plugin logs Vuex mutations to the LogRocket console, giving you context around what led to an error and what state the application was in when an issue occurred.
Modernize how you debug your Vue apps — start monitoring for free.