Editor’s note: This post was last updated on 9 September 2021 to improve code, images, and any outdated information.
This post will show you how to pass data from a parent component child components in Vue.js using Vue slots.
This post is suited for developers of all stages — including beginners — though there are some prerequisites to acknowledge before going through this tutorial.
You will need the following on your machine:
Vue slots are Vue template elements created by the Vue team to provide a platform for template content distribution. It is an implementation of the a content distribution API that was inspired by the Web Components spec draft.
Using Vue slots, you can pass or distribute HTML code across various components in your project.
Content distribution is important for many reasons, some of which have to do with structure. With Vue slots, you can structure an HTML interface (like with TypeScript), which you can then use as a guide to build out your components by template injection.
It is a very scalable and efficient solution for passing down template code from one component to another.
Positioning content is another great use case for Vue slots. You can just create a template and then use another component or the parent component to arrange that template as you want it to appear in the user interface.
If you know about Vue slots, you might wonder if props and slots do the same thing. Well, the central idea of these tools or platforms is to encourage reusability and efficiency of resources. With that in mind, slots and props are similar.
Props deal with passing data objects from component to component, but slots deal with passing template (HTML) content instead from component to component. However, scoped slots act exactly like props; this will be clearly illustrated in this tutorial.
For slots, your child component acts as the interface or structure of how you want your content arranged. It can look like this:
<template> <div> <slot></slot> </div> </template>
The parent component (where the HTML content to be injected into the child component resides) can look like this:
<Test> <h2>Hello World!</h2> </Test>
This combination returns a user interface that looks like this:
<template> <div> <h2>Hello World!</h2> </div> </template>
Notice how the slot on its own serves as a guide for where and how content will be injected — that is the central idea.
If you have followed this post from the start, you will have the Vue starter project open in Visual Studio Code. To illustrate the simple example in the syntax section, our parent component will be the app.vue
file. Open your app.vue
file and copy in this code block:
<template> <div id="app"> <img alt="Vue logo" src="./assets/logo.png"> <Test> <h2>Hello World!</h2> </Test> </div> </template> <script> import Test from './components/Test.vue' export default { name: 'app', components: { Test } } </script>
The child component is the test component, so copy the code block below in the test.vue
file:
<template> <div> <slot></slot> </div> </template> <script> export default { name: 'Test' } </script>
Run the application in the dev environment with this command:
npm run serve
It then renders the following:
Vue allows for more than one slot for a component, which means that you can have any number of slots you want. To test this out, copy this new code block into your test.vue
file:
<template> <div> <slot></slot> <slot></slot> <slot></slot> </div> </template> <script> export default { name: 'Test' } </script>
If you run the application, you can see that hello world
is printed three times. So if you want to add more content — say, a header, a paragraph with text, and then an unordered list — Vue lets us name the scopes so that it can identify the particular scope to display.
Naming the slots in the test.vue
file look like this:
<template> <div> <slot name="header"></slot> <slot name="paragraph"></slot> <slot name="links"></slot> </div> </template> <script> export default { name: 'Test' } </script>
Now, you must also label the HTML elements according to the slot name to display them in. Copy this into the template section of your app.vue
file:
<template> <div id="app"> <img alt="Vue logo" src="./assets/logo.png"> <Test> <h2 slot="header">Hello world!</h2> <p slot="paragraph">Hello, I am a paragraph text</p> <ul slot="links"> <li>Hello, I am a list item</li> <li>Hello, I am a list item</li> </ul> </Test> </div> </template>
V-slots
?When Vue version 2.6 was released, it shipped with a better syntax to reference slot names from child components called v-slot
, which is meant to replace the initial slot syntax. So, instead of a parent component template with slots like this:
<Test> <h1 slot="header">Hello world!</h1> </Test>
From version 3.0, it now looks like this:
<Test v-slot:header> <h1>Hello world!</h1> </Test>
Notice that aside from the minor change in the string from slot
to v-slot
, there is also a major change: the v-slot
can only be defined on templates instead of on any HTML element. This is a big change as it questions the usability of named slots, but as of this writing, slots are still very much part of the documentation.
Imagine a scenario in which the Vue slot can also access a data object in the child component from the parent component — a kind of slot with props capability.
To illustrate this, go ahead and create a data object in the child component by copying the code block below into the test.vue
file:
<template> <div> <slot v-bind:team="team"></slot> <slot name="paragraph"></slot> <slot name="links"></slot> </div> </template> <script> export default { name: 'Test', data(){ return{ team:"FC Barcelona" } } } </script>
Just like normal props, the v-bind
directive binds the team in the data with the prop reference in the parent component. Open your app.vue
file and copy the code block below into the template section:
<template> <div id="app"> <img alt="Vue logo" src="./assets/logo.png"> <Test v-slot="{team}"> <h2>Hello world! my team is {{team}}</h2> </Test> </div> </template>
If you run your application, you see that the data object was successfully passed to the parent component. You can find the complete code for this tutorial here.
This post introduced you to slots in Vue.js and how they are important to content injection. You saw how to set it up, and even how to have more than one slot for a component. You also saw how slots can act as props by scoping. Go ahead and enjoy implementing slots in your workflow.
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.
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 nowJavaScript’s Date API has many limitations. Explore alternative libraries like Moment.js, date-fns, and the new Temporal API.
Explore use cases for using npm vs. npx such as long-term dependency management or temporary tasks and running packages on the fly.
Validating and auditing AI-generated code reduces code errors and ensures that code is compliant.
Build a real-time image background remover in Vue using Transformers.js and WebGPU for client-side processing with privacy and efficiency.