Combining multiple front-end technologies can be challenging due to limited documentation. We’ll solve such a problem by creating a small web application that combines Svelte–a small but powerful front-end technology akin to React and Vue–with the elegant Material Design for Bootstrap 4 & 5 (MDB) framework. It will feature a navigation bar with an embedded dropdown menu that facilitates switching between three different content panes, each populated with stylish MDB components, resulting in an aesthetically appealing and responsive User Interface (UI).
Prerequisites
This article is for the intermediate-to-advanced level web developer who’s conversant in CSS and Bootstrap 5. If you’re not well-versed in either, don’t worry, as this article provides links to useful documentation.
We're going to build our Svelte application using the Svelte REPL sandbox (or just REPL) at svelte.dev. I recommend checking out all the great documentation at svelte.dev, like its Examples section showcasing Svelte's many features, as well as the cool interactive tutorial at learn.svelte.dev.
Here are resources to consider looking at:
- Bootstrap 5
- MDB
- MDBootstrap 5 documentation
- Svelte
Step One – Creating a Svelte/MDB Boilerplate in REPL
When you navigate to the Svelte REPL sandbox and click the REPL link, it takes you to a Hello World application with the following code:
(In App.svelte
)
<script>
let name = 'world';
</script>
<h1>Hello {name}!</h1>
Let's link Bootstrap 5, FontAwesome, a Google font, and MDB stylesheets using a special <svelte:head>
element and the Popper and MDB JavaScripts. Replace this markup with the following:
<script>
// Component initialization code goes here.
</script>
<svelte:head>
<!-- Bootstrap CSS -->
<link
href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css"
rel="stylesheet"
integrity="sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC"
crossorigin="anonymous"
/>
<!-- Font Awesome -->
<link
href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css"
rel="stylesheet"
/>
<!-- Google Fonts -->
<link
href="https://fonts.googleapis.com/css?family=Roboto:300,400,500,700&display=swap"
rel="stylesheet"
/>
<!-- MDB -->
<link
href="https://cdnjs.cloudflare.com/ajax/libs/mdb-ui-kit/7.3.2/mdb.min.css"
rel="stylesheet"
/>
</svelte:head>
<body>
<header class="mb-5">
<!-- navbar -->
</header>
<!-- header -->
<!-- main -->
<main class="container py-3">
<!-- pane content -->
</main>
<!-- main -->
<!-- Bootstrap Bundle with Popper -->
<script
src="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/js/bootstrap.bundle.min.js"
integrity="sha384-MrcW6ZMFYlzcLA8Nl+NtUVF0sA7MsXsP1UyJoMp4YLEuNSfAP+JcXn/tWtIaxVXM"
crossorigin="anonymous"></script>
<!-- MDB -->
<script
type="text/javascript"
src="https://cdnjs.cloudflare.com/ajax/libs/mdb-ui-kit/7.3.2/mdb.umd.min.js"
></script>
</body>
<style>
/* Scripts */
</style>
When you paste code blocks from this article into REPL, it won’t be formatted with the proper whitespace, so you might want to fold the pasted code block, select it, and then tab it in. I hope they consider adding a “format HTML” button.
It’s important to understand that the above-linked files might be obsolete when you read this. I recommend replacing the above
<link>
and<script>
elements with the markup from the MDB installation page. It provides detailed instructions about how to do this.
It’s wise to save this REPL with a meaningful name like Svelte Boilerplate, fork it, and then rename the forked copy to the name of this project. This is akin to creating a commit.
Here is the REPL for this step.
Step Two – Adding Navigation Bar and Cards
Now we'll flesh out our application by adding an MDB navbar, and an accordion as our content for pane 1–the default pane. We'll start with the navigation bar (navbar).
MDB Navigation Bar
Our navbar will have a left-aligned brand logo and a right-aligned dropdown menu. Clicking a menu item switches between panes, and the active pane will be displayed in the dropdown's display text.
We'll build our navbar over several steps, starting with a static navbar containing dummy menu items. Let's start by replacing our <!-- navbar -->
block with the following code:
<!-- navbar -->
<nav class="navbar navbar-expand-lg navbar-light bg-body-tertiary">
<div class="container-fluid">
<a class="navbar-brand" href="navbar_Click">Branding</a>
<!-- Dropdown Menu -->
<ul class="navbar-nav">
<div class="nav-item dropdown pe-3">
<a
data-mdb-dropdown-init
class="nav-link dropdown-toggle"
id="navbarDropdownMenuLink"
role="button"
aria-expanded="false"
>Selected Pane</a>
<!-- nav-link (dropdown button) -->
<ul class="dropdown-menu" aria-labelledby="navbarDropdownMenuLink">
<li class="dropdown-item">
<button
type="button"
class="btn btn-link"
data-mdb-ripple-init
value="Pane 1"
>Pane 1</button>
</li>
<!-- dropdown-item -->
<li class="dropdown-item">
<button
type="button"
class="btn btn-link"
data-mdb-ripple-init
value="Pane 2"
>Pane 2</button>
</li>
<!-- dropdown-item -->
<li class="dropdown-item">
<button
type="button"
class="btn btn-link"
data-mdb-ripple-init
value="Pane 3"
>Pane 3</button>
</li>
<!-- dropdown-item -->
</ul>
<!-- dropdown-menu -->
</ul>
<!-- Dropdown Menu -->
</div>
<!-- container-fluid -->
</nav>
<!-- navbar -->
MDB Accordion
Let’s add an MDB accordion as filler content to pane 1. Replace the <!-- pane content -->
block with the following code, copied from the MDB accordion documentation.
<!-- pane 1 -->
<div class="accordion" id="accordionExampleY">
<div class="accordion-item">
<h2 class="accordion-header" id="headingOneY">
<button data-mdb-collapse-init class="accordion-button" type="button" data-mdb-toggle="collapse"
data-mdb-target="#collapseOneY" aria-expanded="true" aria-controls="collapseOneY">
<i class="fas fa-question-circle fa-sm me-2 opacity-70"></i>Accordion Item #1
</button>
</h2>
<!-- accordion-header -->
<div id="collapseOneY" class="accordion-collapse collapse show" aria-labelledby="headingOneY"
data-mdb-parent="#accordionExampleY">
<div class="accordion-body">
<strong>This is the first item's accordion body.</strong> It is hidden by
default, until the collapse plugin adds the appropriate classes we use to
style each element. These classes control the overall appearance and
the visibility via CSS transitions. You can modify any of this with
custom CSS or overriding our default variables. It's also worth noting that
nearly any HTML can go within the <code>.accordion-body</code>, though the
transition does limit overflow.
</div>
</div>
<!-- accordion-collapse -->
</div>
<!-- accordion-item -->
<div class="accordion-item">
<h2 class="accordion-header" id="headingTwoY">
<button data-mdb-collapse-init class="accordion-button collapsed" type="button" data-mdb-toggle="collapse"
data-mdb-target="#collapseTwoY" aria-expanded="false" aria-controls="collapseTwoY">
<i class="fas fa-question-circle fa-sm me-2 opacity-70"></i>Accordion Item #2
</button>
</h2>
<!-- accordion-header -->
<div id="collapseTwoY" class="accordion-collapse collapse" aria-labelledby="headingTwoY"
data-mdb-parent="#accordionExampleY">
<div class="accordion-body">
<strong>This is the second item's accordion body.</strong> It is hidden by
default, until the collapse plugin adds the appropriate classes we use to
style each element. These classes control the overall appearance and
the visibility via CSS transitions. You can modify any of this with
custom CSS or overriding our default variables. It's also worth noting that
nearly any HTML can go within the <code>.accordion-body</code>, though the
transition does limit overflow.
</div>
</div>
<!-- accordion-collapse -->
</div>
<!-- accordion-item -->
<div class="accordion-item">
<h2 class="accordion-header" id="headingThreeY">
<button data-mdb-collapse-init class="accordion-button collapsed" type="button" data-mdb-toggle="collapse"
data-mdb-target="#collapseThreeY" aria-expanded="false" aria-controls="collapseThreeY">
<i class="fas fa-question-circle fa-sm me-2 opacity-70"></i>Accordion Item #3
</button>
</h2>
<!-- accordion-heading -->
<div id="collapseThreeY" class="accordion-collapse collapse" aria-labelledby="headingThreeY"
data-mdb-parent="#accordionExampleY">
<div class="accordion-body">
<strong>This is the third item's accordion body.</strong> It is hidden by
default, until the collapse plugin adds the appropriate classes we use to
style each element. These classes control the overall appearance and
the visibility via CSS transitions. You can modify any of this with
custom CSS or overriding our default variables. It's also worth noting that
nearly any HTML can go within the <code>.accordion-body</code>, though the
transition does limit overflow.
</div>
</div>
<!-- accordion-collapse -->
</div>
<!-- accordion-item -->
</div>
<!-- accordion -->
<!-- pane 1 -->
Here is the REPL for this step.
Step Three – Populating Navbar Dropdown Menu
Now, we'll use some Svelte template code to dynamically populate the navbar’s dropdown menu and synchronize its display text with the name of the active pane.
First, let's create a ButtonTypes
enumerated type, which represents the currently active pane, and a mode
variable of type ButtonTypes
. Replace your <script>
block with the following code:
<script>
const ButtonTypes = {
PANEL_1: 'Panel 1',
PANEL_2: 'Panel 2',
PANEL_3: 'Panel 3'
};
let mode = ButtonTypes.PANEL_1;
</script>
I use the term “makeshift” because a true enumerated type is only available in TypeScript. Unfortunately, svelte.dev doesn't support TypeScript as of this writing.
Next, let’s dynamically create our dropdown menu items and set the menu's display text to the name of the active pane. Replace the <!-- Dropdown Menu -->
comment block with the following code:
<!-- Dropdown Menu -->
<ul class="navbar-nav">
<div class="nav-item dropdown pe-3">
<a
data-mdb-dropdown-init
class="nav-link dropdown-toggle"
id="navbarDropdownMenuLink"
role="button"
aria-expanded="false"
>{mode}</a>
<!-- nav-link (dropdown button) -->
<ul class="dropdown-menu" aria-labelledby="navbarDropdownMenuLink">
{#each Object.keys(ButtonTypes) as buttonTypeKey}
<li class="dropdown-item">
<button
type="button"
class="btn btn-link"
data-mdb-ripple-init
value={ButtonTypes[buttonTypeKey]}
>{ButtonTypes[buttonTypeKey]}</button>
</li>
<!-- dropdown-item -->
{/each}
</ul>
<!-- dropdown-menu -->
</div>
</ul>
<!-- Dropdown Menu -->
Because ButtonTypes
is an object and not a true enumerated type, we iterate over its properties, assigning each menu item's display text.
Here is the REPL for this step.
Step Four – Switching Between Panes
Now we're going to augment our dropdown menu with functionality to switch between panes. For now we’ll create filler content for panes 2 and 3 with <h2>
elements. (We already have an accordion in pane 1.) Additionally, we’ll add an if statement to our <main>
element that uses our mode
enum to decide which pane to display.
First, let’s create a click event-handler function named navbar_Click
and attach it to each menu item. navbar_Click
changes the active pane by assigning a new value to mode
. Replace your <script>
element with the following code:
<script>
const ButtonTypes = {
PANEL_1: 'Panel 1',
PANEL_2: 'Panel 2',
PANEL_3: 'Panel 3'
};
let mode = ButtonTypes.PANEL_1;
const navbar_Click = function(e) {
console.log(`e.target.value = ${e.target.value}`);
mode = e.target.value;
}
</script>
Next, let’s attach navbar_Click
to each dropdown menu item <button>
as it is created. Replace your <!-- Dropdown Menu -->
block with the following code.
<!-- Dropdown Menu -->
<ul class="navbar-nav">
<div class="nav-item dropdown pe-3">
<a
data-mdb-dropdown-init
class="nav-link dropdown-toggle"
id="navbarDropdownMenuLink"
role="button"
aria-expanded="false"
>{mode}</a>
<!-- nav-link (dropdown button) -->
<ul class="dropdown-menu" aria-labelledby="navbarDropdownMenuLink">
{#each Object.keys(ButtonTypes) as buttonTypeKey}
<li class="dropdown-item">
<button
on:click={navbar_Click}
type="button"
class="btn btn-link"
data-mdb-ripple-init
value={ButtonTypes[buttonTypeKey]}
>{ButtonTypes[buttonTypeKey]}</button>
</li>
<!-- dropdown-item -->
{/each}
</ul>
<!-- dropdown-menu -->
</div>
<!-- nav-item -->
</ul>
<!-- Dropdown Menu -->
Finally, let’s add an if statement to <main>
that uses mode
to determine which pane to display. Update your <main>
element with the following if statement:
<!-- main -->
<main class="container py-3">
{#if mode === ButtonTypes.PANEL_1}
<!-- pane 1 -->
<!-- accordion ... -->
<!-- pane 1 -->
{:else if mode === ButtonTypes.PANEL_2}
<!-- pane 2 -->
<h2>Panel 2</h2>
<!-- pane 2 -->
{:else if mode === ButtonTypes.PANEL_3}
<!-- pane 3 -->
<h2>Panel 3</h2>
<!-- pane 3 -->
{/if}
</main>
<!-- main -->
You’ll want to replace the above
<!-- accordion ... -->
block with either the accordion markup you used earlier in this article, or, preferably, that found in the MDB accordion documentation.
Test the UI and make sure the dropdown menu properly switches between panes.
Here is the REPL for this step.
Step Five – Disabling the Active Panel’s Menu Item
Sometimes it’s useful to disable or hide elements in a UI to prevent invalid actions or give the user hints about how the user interface works. We’ll disable the dropdown menu item representing the active pane, thus helping the user understand which pane is active.
First, let’s add a disableMenuItem
boolean variable to our <script>
. Replace your <script>
with the following code:
<script>
const ButtonTypes = {
PANEL_1: 'Panel 1',
PANEL_2: 'Panel 2',
PANEL_3: 'Panel 3'
};
let mode = ButtonTypes.PANEL_1;
let disableMenuItem = false;
const navbar_Click = function(e) {
console.log(`e.target.value = ${e.target.value}`);
mode = e.target.value;
}
</script>
Next, let's add a disabled
attribute to the <button>
menu item, assigning it to disableMenuItem
. Replace the <!-- Dropdown Menu -->
block with the following code:
<!-- Dropdown Menu -->
<ul class="navbar-nav">
<div class="nav-item dropdown pe-3">
<a
data-mdb-dropdown-init
class="nav-link dropdown-toggle"
id="navbarDropdownMenuLink"
role="button"
aria-expanded="false"
>{mode}</a>
<!-- nav-link (dropdown button) -->
<ul class="dropdown-menu" aria-labelledby="navbarDropdownMenuLink">
{#each Object.keys(ButtonTypes) as buttonTypeKey}
<li class="dropdown-item">
<button
on:click={navbar_Click}
disabled={disableMenuItem = ButtonTypes[buttonTypeKey] === mode}
type="button"
class="btn btn-link"
data-mdb-ripple-init
value={ButtonTypes[buttonTypeKey]}
>{ButtonTypes[buttonTypeKey]}</button>
</li>
<!-- dropdown-item -->
{/each}
</ul>
<!-- dropdown-menu -->
</div>
<!-- nav-item dropdown -->
</ul>
<!-- Dropdown Menu -->
Here is the REPL for this step.
Step Six – Fleshing Out Panels With MDB Components
Now we'll flesh out our UI by adding MDB components, taken directly from the MDB website documentation–to panes 2 and 3. Let's add a card gallery to pane 2 and feature badges to pane 3. (We’ve already added an MDB accordion to pane 1, so we only need content for panes 2 and 3.)
Panel 2 – MDB Cards
Replace your <!-- pane 2 -->
block with the following card gallery markup.
I’ve added whitespace and comments to the following code.
<!-- pane 2 -->
<div class="row row-cols-1 row-cols-md-3 g-4">
<div class="col">
<div class="card">
<img
src="https://mdbcdn.b-cdn.net/img/new/standard/city/041.webp"
class="card-img-top"
alt="Hollywood Sign on The Hill"/>
<div class="card-body">
<h5 class="card-title">Card title</h5>
<p class="card-text">
This is a longer card with supporting text below as a natural lead-in to
additional content. This content is a little bit longer.
</p>
</div>
<!-- card-body -->
</div>
<!-- card -->
</div>
<!-- col -->
<div class="col">
<div class="card">
<img
src="https://mdbcdn.b-cdn.net/img/new/standard/city/042.webp"
class="card-img-top"
alt="Palm Springs Road"/>
<div class="card-body">
<h5 class="card-title">Card title</h5>
<p class="card-text">
This is a longer card with supporting text below as a natural lead-in to
additional content. This content is a little bit longer.
</p>
</div>
<!-- card-body -->
</div>
<!-- card -->
</div>
<!-- col -->
<div class="col">
<div class="card">
<img
src="https://mdbcdn.b-cdn.net/img/new/standard/city/043.webp"
class="card-img-top"
alt="Los Angeles Skyscrapers"/>
<div class="card-body">
<h5 class="card-title">Card title</h5>
<p class="card-text">This is a longer card with supporting text below as a natural lead-in to additional content.</p>
</div>
<!-- card-body -->
</div>
<!-- card -->
</div>
<!-- col -->
<div class="col">
<div class="card">
<img
src="https://mdbcdn.b-cdn.net/img/new/standard/city/044.webp"
class="card-img-top"
alt="Skyscrapers"/>
<div class="card-body">
<h5 class="card-title">Card title</h5>
<p class="card-text">
This is a longer card with supporting text below as a natural lead-in to
additional content. This content is a little bit longer.
</p>
</div>
</div>
<!-- card -->
</div>
<!-- col -->
</div>
<!-- row -->
<!-- pane 2 -->
Panel 3 – MDB Feature Badges
Replace your <!-- pane 3 -->
block with the following feature badges markup:
<!-- pane 3 -->
<section class="mb-5">
<div class="row gx-lg-5">
<div class="col-lg-6 mb-5">
<div class="d-flex align-items-start">
<div class="flex-shrink-0">
<div class="p-3 badge-primary rounded-4">
<i class="fas fa-cloud-upload-alt fa-lg text-primary fa-fw"></i>
</div>
</div>
<div class="flex-grow-1 ms-4">
<p class="fw-bold mb-1">Tutorials</p>
<p class="text-muted mb-1">
Dozens of free tutorials to help you discover the full potential of MDB.
</p>
<small><a href="google.com">Learn more</a></small>
</div>
</div>
</div>
<!-- col -->
<div class="col-lg-6 mb-5">
<div class="d-flex align-items-start">
<div class="flex-shrink-0">
<div class="p-3 badge-primary rounded-4">
<i class="fas fa-database fa-lg text-primary fa-fw"></i>
</div>
</div>
<div class="flex-grow-1 ms-4">
<p class="fw-bold mb-1">Integrations</p>
<p class="text-muted mb-1">
MDB is integrated with all major technologies and tools.
</p>
<small><a href="google.com">Learn more</a></small>
</div>
</div>
</div>
<!-- col -->
<div class="col-lg-6 mb-5">
<div class="d-flex align-items-start">
<div class="flex-shrink-0">
<div class="p-3 badge-primary rounded-4">
<i class="fas fa-stream fa-lg text-primary fa-fw"></i>
</div>
</div>
<div class="flex-grow-1 ms-4">
<p class="fw-bold mb-1">Backend friendly</p>
<p class="text-muted mb-1">
MDB is designed to be seamlessly integrated and used with the backend.
</p>
<small><a href="google.com">Learn more</a></small>
</div>
</div>
</div>
<!-- col -->
<div class="col-lg-6 mb-5">
<div class="d-flex align-items-start">
<div class="flex-shrink-0">
<div class="p-3 badge-primary rounded-4">
<i class="fas fa-copy fa-lg text-primary fa-fw"></i>
</div>
</div>
<div class="flex-grow-1 ms-4">
<p class="fw-bold mb-1">Support and community</p>
<p class="text-muted mb-1">
The MDB team and our global community are there to support you.
</p>
<small><a href="google.com">Learn more</a></small>
</div>
</div>
</div>
<!-- col -->
</div>
<!-- row-->
</section>
<!-- pane 3 -->
Another great benefit of MDB is the relative ease with which you can create responsive web pages. Try horizontally resizing the sandbox Result window and notice how the components resize and reposition as breakpoints are crossed.
Here is the REPL for this step.
Cleaning Up Our Code
Now that we’ve finished creating a winsome UI, we’ll clean up and refactor our code. First, we’ll divide our code into multiple files by using <svelte:component>
.
Step One – Using svelte:component
Svelte offers a very useful <svelte:component>
element that lets us pass a component to display using the this
directive.
Check out this great example of using svelte:component and Working with DOM nodes using the bind:this={dom_node} directive for more about
<svelte:component>
and thethis
directive, respectively.
First, let’s create three new components representing each of our panes, Move your <!-- pane 1 -->
code block into a new Panel1.svelte
, <!-- pane 2 -->
into Panel2.svelte
, and <!-- pane 3 -->
into Panel3.svelte
.
Next, let’s import the new panel components into App.svelte
. Replace your <script>
block with the following code:
(In App.svelte
)
<script>
import Panel1 from './Panel1.svelte';
import Panel2 from './Panel2.svelte';
import Panel3 from './Panel3.svelte';
const ButtonTypes = {
PANEL_1: 'Panel 1',
PANEL_2: 'Panel 2',
PANEL_3: 'Panel 3'
};
let mode = ButtonTypes.PANEL_1;
const navbar_Click = function(e) {
console.log(`e.target.value = ${e.target.value}`);
mode = e.target.value;
}
</script>
Finally, let’s update App.svelte
to use <svelte:component>
elements to display our new pane components. Replace your <main>
with the following code:
(In App.svelte
)
<!-- main -->
<main class="container py-3">
{#if mode === ButtonTypes.PANEL_1}
<!-- pane 1 -->
<svelte:component this={Panel1} />
<!-- pane 1 -->
{:else if mode === ButtonTypes.PANEL_2}
<!-- pane 2 -->
<svelte:component this={Panel2} />
<!-- pane 2 -->
{:else if mode === ButtonTypes.PANEL_3}
<!-- pane 3 -->
<svelte:component this={Panel3} />
<!-- pane 3 -->
{/if}
</main>
<!-- main -->
Here is the REPL for this step.
Step Two – Creating Store For mode
Variable
Sometimes it’s more convenient to use some application-wide state (a global) instead of propagating it from a parent component to a child. Stores are the Svelte equivalent of readable and writable global variables. To use a store, simply import writable
from svelte/store
, and then create a store variable using writable
. Furthermore, you must prefix a store variable with $
when using it.
This is all you need to know about stores for this article. The MDB article Working with Svelte stores provides a thorough explanation of stores.
We need to move some code around. First, let’s create a stores.js
file that contains ButtonTypes
, imports writable
, and redefines mode
as a writable store. Create stores.js
and add the following code:
(In stores.js
)
import { writable } from 'svelte/store';
export let ButtonTypes = {
PANEL_1: 'Panel 1',
PANEL_2: 'Panel 2',
PANEL_3: 'Panel 3'
};
export const mode = writable(ButtonTypes.PANEL_1);
Next, let’s update our App.svelte
by importing mode
and ButtonTypes
from stores.js
.
(In App.svelte
)
<script>
import { mode, ButtonTypes } from './stores.js'
import Panel1 from './Panel1.svelte';
import Panel2 from './Panel2.svelte';
import Panel3 from './Panel3.svelte';
let disableMenuItem = false;
const navbar_Click = function(e) {
console.log(`e.target.value = ${e.target.value}`);
mode = e.target.value;
}
</script>
This will result in a syntax error. To fix it, type CTRL+f
, and then search-and-replace all instances of mode
with $mode
. However, you do not want to apply this change to the mode
in the import statement.
Here is the REPL for this step.
Step Three – Creating a Layout Component
Now we'll learn how to create generic templates using the <slot>
element.
An Example of Using <slot>
Unlike template processors like Python Flask, Svelte doesn't use template inheritance; instead, it uses composition. Therefore, we don’t inherit from a parent template; instead, any component needing some shared layout will contain a reference to a layout component.
We'll digress and walk through an example using <slot>
. First, let's create a BaseLayout component with some shared structure. It will use <slot>
(or <slot />
) elements as placeholders wherein a Page component will inject code.
(In BaseLayout.svelte
)
<script>
export title;
</script>
<div class="layout">
<header>
<h1>{title}</h1>
</header>
<main>
<slot />
</main>
<footer>
<!-- footer content ... -->
</footer>
</div>
This BaseLayout component defines a header, content area, and footer, and an exported title
property.
Now let’s create a Page component that contains BaseLayout component instance and injects code into it.
(In Page.svelte
)
<script>
import BaseLayout from './BaseLayout.svelte';
let title = "Home Page";
</script>
<BaseLayout {title}>
<p>This is the home page content.</p>
</BaseLayout>
Even the most casual observer will notice how we bind Page's title
variable to Layout's title
property using the {title}
directive, illustrating passing state from a parent (Page) component to a child (BaseLayout).
You can find a thorough explanation of passing state between parent and child components in the article Sharing State Between Components.
Using <slot>
in Our Application
Let's use <slot>
in our application. First, create a new Layout.svelte
file and move all the code from App.svelte
into it. Replace the <!-- main -->
block with the following code.
(In Layout.svelte
)
<!-- main -->
<main class="container py-3">
<slot />
</main>
<!-- main -->
Next, replace the <script>
element with the following code:
(In Layout.svelte
)
<script>
import { mode, ButtonTypes } from './stores.js';
// Variables
let disableMenuItem = false;
// DOM event-handlers
const navbar_Click = function(e) {
console.log(`e.target.value = ${e.target.value}`);
$mode = e.target.value;
}
</script>
Great! You refactored a base layout into its own component.
Finally, let’s update App.svelte
. Import Layout, and use it as an element to inject an if statement into its inner HTML. Replace all your App.svelte
code with the following code:
(In App.svelte
)
<script>
import { mode, ButtonTypes } from './stores.js';
import Panel1 from './Panel1.svelte';
import Panel2 from './Panel2.svelte';
import Panel3 from './Panel3.svelte';
import Layout from './Layout.svelte';
</script>
<Layout>
{#if $mode === ButtonTypes.PANEL_1}
<!-- pane 1 -->
<svelte:component this={Panel1} />
<!-- pane 1 -->
{:else if $mode === ButtonTypes.PANEL_2}
<!-- pane 2 -->
<svelte:component this={Panel2} />
<!-- pane 2 -->
{:else if $mode === ButtonTypes.PANEL_3}
<!-- pane 3 -->
<svelte:component this={Panel3} />
<!-- pane 3 -->
{/if}
</Layout>
Check out slot fallbacks and named slots for great examples of using
<slot>
.
Here is the REPL for this step.
Nested components let you compose more complex components, making it easier to understand and maintain your codebase.
Conclusion
We covered a lot in this article. Now you have the tools to create dainty, pleasurable UIs with MDB and compose nested components. Have fun using Svelte, friend. Now leave, please.
Top comments (0)