Vue Js Slots Tutorial

Posted : admin On 7/20/2022
Vue Js Slots Tutorial Average ratng: 4,3/5 356 votes

It tells Vue which named slot to use, exposes the passed props, which we can destructure. This could be a method or data and works a bit like a render-function in React. Dynamic scoped slots. Right, the final piece of the puzzle. Dynamic scoped slots are much like regular scoped slots, but they have a name derived at runtime.

  1. This Vue JS tutorial will help you master not just Vue but other subsidiaries of JavaScript as well. It will act as a stepping stone into the world of JS – React, Angular and Vue. The instructor Abhay Talreja comes with over 10 years of development experience in multiple languages such as Java, Javascript, Grails, iOS and Android.
  2. Introduced with Vue.js 2.3.0, scoped slots have considerably improved component reusability. For example, the renderless component pattern emerged and solved the problem of providing reusable behavior and pluggable presentation. Here we’ll see how to solve the opposite problem: providing reusable presentation and pluggable behavior.
  3. This is where Vue.js slots step in. A Vue.js component can have a number of named slots and when we use that component we can tell what markup should go in each slot. A slot can contain strings, HTML tags or other Vue.js components.
  4. This page assumes you’ve already read the Components Basics.Read that first if you are new to components. In 2.6.0, we introduced a new unified syntax (the v-slot directive) for named and scoped slots. It replaces the slot and slot-scope attributes, which are now deprecated, but have not been removed and are still documented here.The rationale for introducing the new syntax is.

Smashing Newsletter

Every week, we send out useful front-end & UX techniques. Subscribe and get the Smart Interface Design Checklists PDF delivered to your inbox.

Slots are a powerful tool for creating reusable components in Vue.js, though they aren’t the simplest feature to understand. Let’s take a look at how to use slots and some examples of how they can be used in your Vue applications.

Vue Js Slots Tutorial Free

With the recent release of Vue 2.6, the syntax for using slots has been made more succinct. This change to slots has gotten me re-interested in discovering the potential power of slots to provide reusability, new features, and clearer readability to our Vue-based projects. What are slots truly capable of?

If you’re new to Vue or haven’t seen the changes from version 2.6, read on. Probably the best resource for learning about slots is Vue’s own documentation, but I’ll try to give a rundown here.

What Are 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 or make components more generic. The best way to understand them is to see them in action. Let’s start with a simple example:

This component has a wrapper div. Let’s pretend that div is there to create a stylistic frame around its content. This component is able to be used generically to wrap a frame around any content you want. Let’s see what it looks like to use it. The frame component here refers to the component we just made above.

The content that is between the opening and closing frame tags will get inserted into the frame component where the slot is, replacing the slot tags. This is the most basic way of doing it. You can also specify default content to go into a slot simply by filling it in:

So now if we use it like this instead:

The default text of “This is the default content if nothing gets specified to go here” will show up, but if we use it as we did before, the default text will be overridden by the img tag.

Vue Js Slots Tutorial

Multiple/Named Slots

You can add multiple slots to a component, but if you do, all but one of them is required to have a name. If there is one without a name, it is the default slot. Here’s how you create multiple slots:

We kept the same default slot, but this time we added a slot named header where you can enter a title. You use it like this:

Just like before, if we want to add content to the default slot, just put it directly inside the titled-frame component. To add content to a named slot, though, we needed to wrap the code in a template tag with a v-slot directive. You add a colon (:) after v-slot and then write the name of the slot you want the content to be passed to. Note that v-slot is new to Vue 2.6, so if you’re using an older version, you’ll need to read the docs about the deprecated slot syntax.

Scoped Slots

One more thing you’ll need to know is that slots can pass data/functions down to their children. To demonstrate this, we’ll need a completely different example component with slots, one that’s even more contrived than the previous one: let’s sorta copy the example from the docs by creating a component that supplies the data about the current user to its slots:

This component has a property called user with details about the user. By default, the component shows the user’s last name, but note that it is using v-bind to bind the user data to the slot. With that, we can use this component to provide the user data to its descendant:

To get access to the data passed to the slot, we specify the name of the scope variable with the value of the v-slot directive.

There are a few notes to take here:

  • We specified the name of default, though we don’t need to for the default slot. Instead we could just use v-slot='slotProps'.
  • You don’t need to use slotProps as the name. You can call it whatever you want.
  • If you’re only using a default slot, you can skip that inner template tag and put the v-slot directive directly onto the current-user tag.
  • You can use object destructuring to create direct references to the scoped slot data rather than using a single variable name. In other words, you can use v-slot='{user}' instead of v-slot='slotProps' and then you can use user directly instead of slotProps.user.

Taking those notes into account, the above example can be rewritten like this:

A couple more things to keep in mind:

  • You can bind more than one value with v-bind directives. So in the example, I could have done more than just user.
  • You can pass functions to scoped slots too. Many libraries use this to provide reusable functional components as you’ll see later.
  • v-slot has an alias of #. So instead of writing v-slot:header='data', you can write #header='data'. You can also just specify #header instead of v-slot:header when you’re not using scoped slots. As for default slots, you’ll need to specify the name of default when you use the alias. In other words, you’ll need to write #default='data' instead of #='data'.

There are a few more minor points you can learn about from the docs, but that should be enough to help you understand what we’re talking about in the rest of this article.

What Can You Do With Slots?

Slots weren’t built for a single purpose, or at least if they were, they’ve evolved way beyond that original intention to be a powerhouse tool for doing many different things.

Reusable Patterns

Components were always designed to be able to be reused, but some patterns aren’t practical to enforce with a single “normal” component because the number of props you’ll need in order to customize it can be excessive or you’d need to pass large sections of content and potentially other components through the props. Slots can be used to encompass the “outside” part of the pattern and allow other HTML and/or components to placed inside of them to customize the “inside” part, allowing the component with slots to define the pattern and the components injected into the slots to be unique.

For our first example, let’s start with something simple: a button. Imagine you and your team are using Bootstrap*. With Bootstrap, your buttons are often strapped with the base `btn` class and a class specifying the color, such as `btn-primary`. You can also add a size class, such as `btn-lg`.

* I neither encourage nor discourage you from doing this, I just needed something for my example and it’s pretty well known.

Let’s now assume, for simplicity’s sake that your app/site always uses btn-primary and btn-lg. You don’t want to always have to write all three classes on your buttons, or maybe you don’t trust a rookie to remember to do all three. In that case, you can create a component that automatically has all three of those classes, but how do you allow customization of the content? A prop isn’t practical because a button tag is allowed to have all kinds of HTML in it, so we should use a slot.

Now we can use it everywhere with whatever content you want:

Of course, you can go with something much bigger than a button. Sticking with Bootstrap, let’s look at a modal, or least the HTML part; I won’t be going into functionality… yet.

Now, let’s use this:

The above type of use case for slots is obviously very useful, but it can do even more.

Reusing Functionality

Vue components aren’t all about the HTML and CSS. They’re built with JavaScript, so they’re also about functionality. Slots can be useful for creating functionality once and using it in multiple places. Let’s go back to our modal example and add a function that closes the modal:

Now when you use this component, you can add a button to the footer that can close the modal. Normally, in the case of a Bootstrap modal, you could just add data-dismiss='modal' to a button, but we want to hide Bootstrap specific things away from the components that will be slotting into this modal component. So we pass them a function they can call and they are none the wiser about Bootstrap’s involvement:

Renderless Components

And finally, you can take what you know about using slots to pass around reusable functionality and strip practically all of the HTML and just use the slots. That’s essentially what a renderless component is: a component that provides only functionality without any HTML.

Making components truly renderless can be a little tricky because you’ll need to write render functions rather than using a template in order to remove the need for a root element, but it may not always be necessary. Let’s take a look at a simple example that does let us use a template first, though:

This is an odd example of a renderless component because it doesn’t even have any JavaScript in it. That’s mostly because we’re just creating a pre-configured reusable version of a built-in renderless function: transition.

Yup, Vue has built-in renderless components. This particular example is taken from an article on reusable transitions by Cristi Jora and shows a simple way to create a renderless component that can standardize the transitions used throughout your application. Cristi’s article goes into a lot more depth and shows some more advanced variations of reusable transitions, so I recommend checking it out.

For our other example, we’ll create a component that handles switching what is shown during the different states of a Promise: pending, successfully resolved, and failed. It’s a common pattern and while it doesn’t require a lot of code, it can muddy up a lot of your components if the logic isn’t pulled out for reusability.

So what is going on here? First, note that we are receiving a prop called promise that is a Promise. In the watch section we watch for changes to the promise and when it changes (or immediately on component creation thanks to the immediate property) we clear the state, and call then and catch on the promise, updating the state when it either finishes successfully or fails.

Then, in the template, we show a different slot based on the state. Note that we failed to keep it truly renderless because we needed a root element in order to use a template. We’re passing data and error to the relevant slot scopes as well.

And here’s an example of it being used:

We pass in somePromise to the renderless component. While we’re waiting for it to finish, we’re displaying “Working on it…” thanks to the pending slot. If it succeeds we display “Resolved:” and the resolution value. If it fails we display “Rejected:” and the error that caused the rejection. Now we no longer need to track the state of the promise within this component because that part is pulled out into its own reusable component.

So, what can we do about that span wrapping around the slots in promised.vue? To remove it, we’ll need to remove the template portion and add a render function to our component:

There isn’t anything too tricky going on here. We’re just using some if blocks to find the state and then returning the correct scoped slot (via this.$scopedSlots['SLOTNAME'](...)) and passing the relevant data to the slot scope. When you’re not using a template, you can skip using the .vue file extension by pulling the JavaScript out of the script tag and just plunking it into a .js file. This should give you a very slight performance bump when compiling those Vue files.

This example is a stripped-down and slightly tweaked version of vue-promised, which I would recommend over using the above example because they cover over some potential pitfalls. There are plenty of other great examples of renderless components out there too. Baleada is an entire library full of renderless components that provide useful functionality like this. There’s also vue-virtual-scroller for controlling the rendering of list item based on what is visible on the screen or PortalVue for “teleporting” content to completely different parts of the DOM.

I’m Out

Vue’s slots take component-based development to a whole new level, and while I’ve demonstrated a lot of great ways slots can be used, there are countless more out there. What great idea can you think of? What ways do you think slots could get an upgrade? If you have any, make sure to bring your ideas to the Vue team. God bless and happy coding.

(dm, il)

Vue 3 has not been officially released yet, but the maintainers have released beta versions for us punters to try and provide feedback on.

If you're wondering what the key features and main changes of Vue 3 are, I'll highlight them in this article by walking you through the creation of a simple app using Vue 3 beta 9.

I'm going to cover as much new stuff as I can including fragments, teleport, the Composition API, and several more obscure changes. I'll do my best to explain the rationale for the feature or change as well.

What we'll build

We're going to build a simple app with a modal window feature. I chose this because it conveniently allows me to showcase a number of Vue 3 changes.

Here's what the app looks like in it's opened and closed states so you can picture in your mind what we're working on:

Vue 3 installation and setup

Rather than installing Vue 3 directly, let's clone the project vue-next-webpack-preview which will give us a minimal Webpack setup including Vue 3.

Once that's cloned and the NPM modules are installed, all we need to do is remove the boilerplate files and create a fresh main.js file so we can create our Vue 3 app from scratch.

Now we'll run the dev server:

Creating a new Vue 3 app

Straight off the bat, the way we bootstrap a new Vue app has changed. Rather than using new Vue(), we now need to import the new createApp method.

We then call this method, passing our Vue instance definition object, and assign the return object to a variable app.

Next, we'll call the mount method on app and pass a CSS selector indicating our mount element, just like we did with the $mount instance method in Vue 2.

src/main.js

Reason for change

With the old API, any global configuration we added (plugins, mixins, prototype properties etc) would permanently mutate global state. For example:

Vue Js Slots Tutorial

src/main.js

This really shows up as an issue in unit testing, as it makes it tricky to ensure that each test is isolated from the last.

Under the new API, calling createApp returns a fresh app instance that will not be polluted by any global configuration applied to other instances.

Learn more: Global API change RFC.

Adding state properties

Our modal window can be in one of two states - opened, or closed. Let's manage this with a boolean state property modalOpen which we'll give an initial value of false.

Under Vue 2, we could do this by creating a data property on our app instance and assigning an object to this where our modalOpen property would be declared i.e.:

src/main.js

This is no longer allowed. Instead, data must be assigned a factory function which returns the state object.

This is what you had to do for Vue components, but now it's enforced for Vue app instances as well.

src/main.js

Reason for change

The advantage of using an object for data rather than a factory function is that, firstly, it was syntactically simpler, and secondly, you could share top-level state between multiple root instances e.g.:

src/main.js

The use case for this is rare and can be worked around. Since having two types of declarations is not beginner-friendly, it was decided to remove this feature.

Learn more: Data object declaration removed RFC

Before we move on, let's also add a method to toggle the modalOpen value. This is no different from Vue 2.

src/main.js

Using a root component

If you go to the browser now and check the console, you'll see the warning 'Component is missing render function', since we haven't yet defined a template for the root instance.

The best practice for Vue 2 is to create a minimal template for the root instance and create an App component where the main app markup will be declared.

Let's do that here, as well.

Now we can get the root instance to render that component. The difference is that with Vue 2, we'd normally use a render function for doing this:

src/main.js

We can still do that, but Vue 3 has an even easier way - making App a root component. To do this, we can remove the root instance definition and instead pass the App component.

src/main.js

This means the App component is not just rendered by the root instance but is the root instance.

While we're at it, let's simply the syntax a little by removing the app variable:

src/main.js

Moving over to the root component now, let's re-add the state and method to this component:

src/App.vue

Let's also make a new component for the modal feature:

For now, we'll provide a minimal template including a slot for content. This ensures our modal is reusable. We'll add more to this component later.

src/Modal.vue

Multi-root templates

Let's now create the template for our root component. We'll create a button to open the modal which will trigger the toggleModalState method.

We'll also use our just-created modal component which will be rendered conditional on the value of modalOpen. Let's also insert a paragraph of text into the slot for content.

src/App.vue

Vue Js Slots Tutorial Download

Notice anything odd about this template? Look again. I'll wait.

That's right - there are two root elements. In Vue 3, thanks to a feature called fragments, it is no longer compulsory to have a single root element!

Never miss a new post!

Get our latest post in your inbox every Tuesday by subscribing to the Vue.js Developers Newsletter .

This subscription also includes Vue.js Developers promotional emails. You can opt-out at any time. View our privacy policy .

This form is protected by reCAPTCHA. The Google privacy policy and terms of service apply.

Refactoring with Composition API

The flagship feature of Vue 3 is the Composition API. This new API allows you to define component functionality using a setup function rather than with properties you add to the component definition object.

Let's now refactor our App component to use the Composition API.

Before I explain the code, be clear that all we're doing is refactoring - the functionality of the component will be the same. Also note that the template is not changed as the Composition API only affects the way we define the component functionality, not the way we render it.

src/App.vue

setup method

Firstly, notice we import the ref function which allows us to define a reactive variable modalOpen. This variable is equivalent to this.modalOpen.

The toggleModalState method is just a plain JavaScript function. However, notice that to change the value of modalOpen in the method body, we need to change its sub-property value. That's because reactive variables created using ref are wrapped in an object. This is necessary to retain their reactivity as they're passed around.

It's best to consult the Vue Composition API docs if you want a detailed explanation of how refs work.

Finally, we return modalOpen and toggleModalState from the setup method, as these are the values that get passed to the template when it's rendered.

Reason for change

Keep in mind that the Composition API is not a change as it's purely optional to use. The main motivation is to allow for better code organization and the reuse of code between components (as mixins are essentially an anti-pattern).

You'd be correct in thinking that refactoring the App component in this example to use the Composition API is unnecessary. But, if this were a much larger component, or we needed to share its features with other components, that's when you'd see its usefulness.

Providing a more in-depth example is beyond the scope of this blog post, so if you're interested to learn more about uses of the new API, check out my other article When To Use The New Vue Composition API (And When Not To).

Teleporting content

If you've ever created a modal feature before, you'll know that it's commonly positioned just before the closing </body> tag.

This is done because modals usually have a page-covering background (see the image at the beginning if you don't know what I mean). To implement this with CSS, you don't want to have to deal with parent elements positioning and z-index stacking context, and so the simplest solution is to put the modal at the very bottom of the DOM.

This creates an issue with Vue.js, though, which assumes the UI will be built as a single tree of components. To allow segments of the tree to be moved elsewhere in the DOM, a new teleport component has been added in Vue 3.

To use the teleport, let's first add an element to the page where we want our modal content to be moved to. We'll go to index.html and place a div with ID modal-wrapper adjacent to Vue's mounting element.

index.html

Now, back in App.vue, we're going to wrap the modal content in the teleport component. We'll also need to specify a to attribute which will be assigned a query selector identifying the target element, in this case, #modal-wrapper.

src/App.vue

And that's it. Any content within the teleport will be rendered within the target element. However, it will still function like it was in it's original position in the hierarchy (regarding props, events etc).

So after you've saved your code, reload the page, inspect the DOM in dev tools, and be amazed!

Learn more: Teleport RFC

Emitting an event

Let's now add a button to our modal allowing it to be closed. To do this, we're going to add a button element to the modal tempate with a click handler that emits an event close.

src/Modal.vue

This event is will then be captured by the parent component and will toggle the value of modalOpen, logically making it false and causing the window to close.

src/App.vue

So far, this feature is identical as it would be in Vue 2. However, in Vue 3 it's now recommended that you explicitly state a component's events using the new emits component option. Just like with props, you can simply create an array of strings to name each event the component will emit.

src/Modal.vue

Reason for change

Imagine opening the file of a component that someone else wrote, and seeing its props and events explicitly declared. Immediately, you would understand the interface of this component i.e. what it's meant to send and receive.

In addition to providing self-documenting code, you can also use the events declaration to validate your event payload, though I couldn't find a reason to do that in this example.

Vue Js Tutorial Point

Learn more: Emits Option RFC

Styling slot content

To make our modal reusable, we've provided a slot for content. Let's begin to style that content by adding a style tag to the component.

It's a good practice to use scoped CSS in our components to ensure the rules we provide don't have unintended effects on other content in the page.

Let's make it so any paragraph text that gets put into the slot is italic. To do this, we'll create a new CSS rule using the p selector.

src/Modal.vue

If you try this, you'll see that it doesn't work. The issue is that scoped styling is determined at compile time when the slot content still belongs to the parent.

The solution provided by Vue 3 is to provide a pseudo selector ::v-slotted() allowing you to target slot content with scoped rules in the component providing the slot.

Here's how we use it:

src/Modal.vue

Vue 3 also includes some other new scoped styling selectors ::v-deep and ::v-global which you can learn more about here: Scoped Styles RFC

Other changes

Well, that's all the new features I could feasibly cover in a simple example. I got most of the main ones in, but here are some that I thought important enough to mention before concluding the article that you can research for yourself:

Vue Js Slots Tutorial Software

Added:

Removed:

  • Event interface for components (no more event bus!)

Changed:

There are also various changes regarding Vue Router that you can check out here.