Home / Blog / Techniques for Sharing Data between Vue.js Components
Techniques for Sharing Data between Vue.js Components

Techniques for Sharing Data between Vue.js Components

Charles Allotey
Charles Allotey
November 16th 2022

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!

Props

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

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

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.

slots.dbdaf1e8.png

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.

Stores

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

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.

Conclusion

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.

Start learning Vue.js for free

Charles Allotey
Charles Allotey
Charles is a Frontend Developer at Vueschool. Has a passion for building great experiences and products using Vue.js and Nuxt.

Latest Vue School Articles

Vue.js and HTML Injection Explained

Vue.js and HTML Injection Explained

Learn why v-html can be dangerous. Plus, strategies for avoiding the risk while still providing rich user interfaces
Daniel Kelly
Daniel Kelly
Tightly Coupled Components Vue Components with Provide/Inject

Tightly Coupled Components Vue Components with Provide/Inject

In this article, learn how to create tightly coupled components with Vue’s provide/inject functions. This component design strategy is great for creating components that are intuitive to use together and rely on shared state!
Daniel Kelly
Daniel Kelly

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 120.000 users have already joined us. You are welcome too!

Follow us on Social

© All rights reserved. Made with ❤️ by BitterBrains, Inc.