With the growing use of component-based architectures, large and complex apps are becoming more common. Larger applications are broken into small reusable chunks that makes it easier to build, maintain, test and understand. As this is done there is a need to share data between these pieces to create functionality and interactivity.
In this article, you’ll learn about the various techniques data is shared between Vue.js components. The techniques in this article are fundamental, so if you’re new to Vue.js or you are looking to pick up new information then you should definitely read on!
The first technique for passing data is with props. They allow us to transfer data from a parent to a child component. When we build component applications we form a component tree architecture ie. we have smaller components embedded in bigger components which are all then connected to our root component.
Props is a unidirectional Data Transfer Technique. We can only transfer data from Parent Component to child component so a state can only be changed from our parent component.
Props are added to our component via the template section.
//~/parentComponent.vue
<template>
<child-component myprop="hello world"></child-component>
</template>
In this example, we are passing the prop myprop
with a value of "hello world"
to our child component. We will then be able to access this value from inside of the child-component
by initializing our props object in the script tag of our child component .vue
file
//~/childcomponent.vue
<template>
<div>
{{ myprop }}
</div>
</template>
<script setup>
const props = defineProps({
myprop:String
})
</script>
Our myprop
key has a value of String which is the constructor function of the expected type. Props can be of type String, Number, Boolean, Array or, Object.
Emits or Component Events can be used to share data from a child component to its parent component. But this can only be achieved by triggering events from your child component. I use emits to notify my parent component that something has happened in my child component.
Lets jump right to an example;
//~/childcomponent.vue
<script setup>
const username = ref("");
</script>
<template>
<div>
<form action="submit" @submit.prevent="$emit('changeUsername', username)">
<input
type="text"
v-model="username"
placeholder="Enter your name" />
<button
type="sumbit"
>
Change Username
</button>
</form>
<p>Value: {{ username }}</p>
</div>
</template>
For our example, our child component is a basic form which will receive the username
of a test user by input. On submission we emit a changeUsername
event to our parent component with the username
value to update our username
state.
//~/parentComponent.vue
<script setup>
const username = ref("");
</script>
<template>
<div>
<ChildComponent
@changeUsername="
(payload) => {
username = payload;
}
"
/>
<p class="username">Hello, {{ username }}</p>
</div>
</template>
Slots are a mechanism for Vue components that allows you to compose your components in a way other than the strict parent-child relationship. Slots give you an outlet to place content in new places of our child component or make components more generic. Slots are great for creating layouts.
The best way to understand them is to see them in action. Let’s start with a simple example:
//~/button.vue
<template>
<div>
<button><slot></slot></button>
</div>
</template>
<div>
<Button1>Button first</Button1>
<Button1
><span>Button with icon</span>
<span
><img src="..pathto/someIcon.svg" alt="anyIcon" /> </span
></Button1>
</div>
</template>
From our example we notice that we can reuse our button component and insert dynamic data into it without affecting the original component.
As our application grows in size and complexity, passing data through components can become messy. We will have to pass data from a parent component to a child component which may be deeply nested in the component tree. Stores introduce an advanced method of passing data across components by eliminating the problem of prop drilling. Prop drilling refers to transporting data or states as props to the intended destination through intermediate components.
With stores, our states or data are stored in a centralized point to be accessed by any components irrespective of their hierarchy in the component tree. This is a common way of handling states for big Vue.js applications. Popular state management tools for Vue.js include Pinia and Vuex. For our basic example, we will use Pinia which is an amazing state management tool.
First Let’s add Pinia into our Vue.js application
//yarn
yarn add pinia
//or with npm
npm install pinia
//instructing vue to use pinia
//~app.vue
import { createPinia } from 'pinia'
app.use(pinia)
Let’s define our store
//~store/testStore.js
import {defineStore} from 'pinia'
export const useTestStore = defineStore('test', {
state: () => {
return{
username: null
}
},
actions:{
changeUsername (payload) {
this.username = payload
}
}
})
Our store contains a state which is the central data point of our store and an action which is a method to change the state.
Now let’s try to access our state from a component. We’ll use the composition api for this tutorial. To find out how you can access the store using the options api you can check out the Pinia Documentation .
//~index.vue
<script setup>
import { useTestStore } from "~~/store/testStore";
const store = useTestStore();
</script>
<template>
<div>
<ChildComponent />
<p class="username">Hello, {{ store.username }}</p>
</div>
</template>
Now we are able to view username
in our DOM.
Next is to use our form in the child component to change the state username
in our store using our changeUsername
action.
//~childcomponent.vue
<script setup>
import { useTestStore } from "~~/store/testStore";
const store = useTestStore();
const username = ref("");
</script>
<template>
<div>
<form action="submit" @submit.prevent="store.changeUsername(username)">
<input type="text" v-model="username" placeholder="Enter your name" /><button
type="sumbit"
>
Change Username
</button>
</form>
<p>Value: {{ username }}</p>
</div>
</template>
Provide and Inject technique is also another useful technique of preventing prop drilling when building complex Vue.js applications. With this technique the parent component can provide dependencies for all its child components. This means that any component in the component tree, regardless of how deep it is, can inject dependencies that are provided by components higher up in the component chain.
Let’s jump into an example
To provide data to a component's descendants, use the provide()
function
The provide()
function accepts two arguments. The first argument is called the injection key
which can be a string
or a Symbol
. The second is the data or state we want to provide to our child components.
//~parentcomponent.vue
<template>
<div>
<form action="submit">
<input type="text" v-model="username" placeholder="Enter your name" />
<button
type="sumbit"
>
Change Username
</button>
</form>
<display-child />
</div>
</template>
<script setup>
import { ref, provide } from "vue";
const username = ref("");
provide("username", username);
</script>
To inject data provided by an ancestor component, use the inject()
function
//~displayChild.vue
<template>
<div>
<p>Value: {{ username }}</p>
</div>
</template>
<script setup>
import { inject } from "vue";
const username = inject("username");
</script>
Let’s check if everything works.
Hope you enjoyed reading this article on Sharing Data between Vue.js Components. Components are very essential in creating amazing Vue.js applications. Knowing when and where to use these various techniques for passing data between your components is key to making you an awesome Vue.js developer.
Thinking of how to get started? Our Vue.js 3 Masterclass Course has everything you need and more on building awesome components for your next Vue.js project.
Our goal is to be the number one source of Vue.js knowledge for all skill levels. We offer the knowledge of our industry leaders through awesome video courses for a ridiculously low price.
More than 200.000 users have already joined us. You are welcome too!
© All rights reserved. Made with ❤️ by BitterBrains, Inc.