Svelte.js 3 Component Slots Tutorial
In this Svelte tutorial we learn how to embed content in a child componet through its parent with component slots.
We cover slots and how to override them, default slot content, named slots, slot props and more.
- Lesson Video
- Lesson Project
- What are Component Slots in Svelte?
- How to create a slot in a child component in Svelte
- How to override slot content in a parent component in Svelte
- How to define default content for a slot in Svelte
- How to name and use multiple slots in a single component in Svelte
- How to define and use slot props in Svelte
Lesson Video
If you prefer to learn visually, you can watch this lesson in video format.
Lesson Project
If you want to follow along with the examples in this lesson, you’ll need a Svelte app that was cloned with degit or scaffolded with Vite
If you already have an app from previous lessons, you can use that instead.
What are Component Slots in Svelte?
In the Component Props lesson, we learned that we can pass data from a parent component to a child with props.
While props are great for reusability, it creates a strict parent-child relationship. The parent is allowed to pass values to the child, but the child will always be in control of the HTML.
Slots allow the parent component to embed content in a child component. This content may include HTML elements.
How to create a slot in a child component in Svelte
A slot is a tag we can specify in the child component, then overwrite with content from inside the parent.
To create a slot, we add either a self-closing or open-and-close slot tag in the child component’s markup where we want the overriding content from the parent to appear.
<slot />
<!-- or -->
<slot></slot>
For our example, we’ll use the root App component as the parent and create the following component as the child.
- /src/components/Card.svelte
The project should look similar to the following.
project-folder/
├── src/
| ├── components/
| | └── Card.svelte
| └── App.svelte
Our new component will add a card wrapper element with some styling. Inside the element we’ll add a self-closing slot tag.
<div class="card">
<slot />
</div>
<style>
.card {
max-width:250px;text-align:center;
margin:2rem auto;padding:.6rem .8rem;
border: .1rem solid #94A3B8;color:#64748B;
}
</style>
How to override slot content in a parent component in Svelte
To override a child component’s slot, we change the component instance from a self-closing tag to open-and-close tags.
We can then specify the overriding content inside the tags.
<Component>
<!-- overriding content -->
</Component>
As an example, let’s override our Card component’s slot with a heading in the root App component.
<script>
import Card from './components/Card.svelte'
</script>
<Card>
<!-- overriding content -->
<h1>Hello there</h1>
</Card>
If we run the example in the browser, we should see the heading we used to override the Card slot.
How to define default content for a slot in Svelte
Svelte allows us to specify default content for when the parent doesn’t explicitly override the component.
To do that, we use open-and-close slot tags in the child component and specify the default content inside them.
<slot>
<!-- default content -->
</slot>
To demonstrate, let’s add a paragraph with some text as default content to our Card component.
<div class="card">
<slot>
<p>Default content</p>
</slot>
</div>
<style>
.card {
max-width:250px;text-align:center;
margin:2rem auto;padding:.6rem .8rem;
border: .1rem solid #94A3B8;color:#64748B;
}
</style>
In the root App component, we’ll change our the Card instance to a self-closing tag with no overriding content.
<script>
import Card from './components/Card.svelte'
</script>
<Card />
If we run the example in the browser, we’ll see the default content we defined in the Card component.
How to name and use multiple slots in a single component in Svelte
In many cases we’ll want to have predefined structure and content in the child component, and only control some of that content from the parent.
For example, a blog post card’s button can be the same for all the posts but the heading and image should be different.
Svelte allows us to have multiple slots in our child component provided we name them.
To name a slot, we attach the name attribute to the slot element and specify a unique name as its value.
<slot name="slotName"></slot>
As an example, let’s add slots for a title and an image in our Card component. Finally, we’ll add a button without a slot because it won’t need to change.
<div class="card">
<slot name="cardTitle">
<h3>Fallback Title</h3>
</slot>
<slot name="cardImage">
<p>Fallback content</p>
</slot>
<button>Read more...</button>
</div>
<style>
.card {
max-width:250px;text-align:center;
margin:2rem auto;padding:.6rem .8rem;
border: .1rem solid #94A3B8;color:#64748B;
}
button { margin:1rem 0 }
</style>
If we run the example in the browser, we’ll see the fallback content.
To override a named slot in the parent, we use the slot attribute with the name of the slot on the outermost element we override with.
<Component>
<element slot="slotName">
<!-- content -->
</element>
</Component>
As an example, let’s override the title with a heading element and the image with an image element in the root App component.
<script>
import Card from './components/Card.svelte'
</script>
<Card>
<h3 slot="cardTitle">Post title</h3>
<img slot="cardImage" src="https://picsum.photos/200" alt>
</Card>
If we run the example in the browser, it should display the title and image in the correct places.
How to conditionally render content based on the parent override
Sometimes it will be necessary to only render content if a slot is overridden in the parent.
For example, let’s say we have an excerpt for our blog post in the card and we only want to show that excerpt if the image has been overriden in the parent.
Svelte gives us the $$slots object that contains all the slot names that the parent overrides. The names can be access through dot notation as the condition in an if block to render or not render the content inside.
{#if $$slots.slotName }
<!-- content -->
{/if}
Let’s use our excerpt example from above and only render it if the cardImage slot is overridden in the parent.
<div class="card">
<slot name="cardTitle">
<h3>Fallback Title</h3>
</slot>
<slot name="cardImage">
<p>Fallback content</p>
</slot>
{#if $$slots.cardImage}
<p>Post excerpt</p>
{/if}
<button>Read more...</button>
</div>
<style>
.card {
max-width:250px;text-align:center;
margin:2rem auto;padding:.6rem .8rem;
border: .1rem solid #94A3B8;color:#64748B;
}
button { margin:1rem 0 }
</style>
If we run the example in the browser, we’ll see the excerpt because the parent currently overrides the image.
Let’s remove the overriding image from the root App component.
<script>
import Card from './components/Card.svelte'
</script>
<Card>
<h3 slot="cardTitle">Post title</h3>
</Card>
When we run the example in the browser, both the image and the excerpt doesn’t render.
How to define and use slot props in Svelte
In Svelte, we can define props in a child component’s slot, then access the prop values in the parent when it overrides that slot.
To define a slot prop, we simply add it to a slot tag in the child component.
<slot propName="propValue" />
To demonstrate, let’s add an author slot to our example with two props. To keep the example simple, we’ll remove all the other slots.
<div class="card">
<slot
name="author"
firstName="John"
lastName="Doe"
/>
</div>
<style>
.card {
max-width:250px;text-align:center;
margin:2rem auto;padding:.6rem .8rem;
border: .1rem solid #94A3B8;color:#64748B;
}
</style>
To access the slot prop values in the parent, we use the let directive on an element in the component instance we’re overriding.
<Component>
<element let:propName>
<!-- use propName -->
</element>
</Component>
As an example, let’s access the first and last name props in the root App component in a paragraph.
<script>
import Card from './components/Card.svelte'
</script>
<Card>
<p slot="author" let:firstName let:lastName>
Written by {firstName} {lastName}
</p>
</Card>
If we run the example in the browser, we’ll see the two names in the card.