Creating a Sidebar Menu Layout with SvelteKit
Tags: css, svelte, sveltekit
Published: 2023-01-31 00:50:08

by [Shinichi Okada](https://blog.openreplay.com/authors/shinichi-okada)


In this article, we are going to create a responsive sidebar menu layout using SvelteKit 1.0.0 and [](https://flowbite-svelte.com/)[Flowbite-Svelte](https://flowbite-svelte.com/). You can find the demo at [https://flowbite-sveltekit-responsive-sidebar-layout.vercel.app/.](https://sveltekit-sidebar-layout.vercel.app/)
Flowbite-Svelte is an official [](https://flowbite.com/)[Flowbite](https://flowbite.com/) UI component library for Svelte. It has more than 50 UI components, and you can build websites faster.

Let's get started.

## Installing all packages

We are going to create a new SvelteKit app:

```
npm create svelte@latest responsive-sidebar-layout
cd responsive-sidebar-layout
npm i
```

![1](https://blog.openreplay.com/images/creating-a-sidebar-menu-layout-with-sveltekit/images/image01.png)

Tailwind CSS is a utility-first CSS framework. You can add it directly to your markup.
Let's install Tailwind CSS:

```
npx svelte-add@latest tailwindcss
npm i
```

Update tailwind.config.cjs:

```javascript
const config = {
  content: [
    "./src/**/*.{html,js,svelte,ts}",
    "./node_modules/flowbite-svelte/**/*.{html,js,svelte,ts}",
  ],
  theme: {
    extend: {},
  },
  plugins: [
    require('flowbite/plugin')
  ],
  darkMode: 'class',
};
module.exports = config;
```

Installing Flowbite, Flowbite-Svelte, and dependencies:

```
npm i -D flowbite flowbite-svelte classnames @popperjs/core 
```

At the time of writing this, you may need to use `esbuild@0.16.8`:

``` 
npm i -D esbuild@0.16.8
```

Start the server and check if it works without any errors:

```
npm run dev
```

You should see the following at http://127.0.0.1:5173/.

![2](https://blog.openreplay.com/images/creating-a-sidebar-menu-layout-with-sveltekit/images/image02.png)


We will use Svelte-Heros SVG icons for menu icons and our logo. Let's install it:

```
npm i -D svelte-heros-v2
```

Here we installed Hero icons version 2 with Svelte-Heros-v2.

## Creating dummy pages

We need some dummy pages. Create `src/routes/pages/about/+page.svelte`, `src/routes/blog/2022-01-01/+page.svelte`, and `src/routes/blog/2022-01-2/+page.svelte`.

Instead of creating separate directories for each page, you can use a script to add multiple files to a directory at once. This can save time and effort, especially if you have many pages. I will demonstrate this method in a future article, so stay tuned!


![3](https://blog.openreplay.com/images/creating-a-sidebar-menu-layout-with-sveltekit/images/image03.png)


You can add any content for each article. Now you can visit http://127.0.0.1:5173/pages/about, http://127.0.0.1:5173/blog/2023-01-01, and http://127.0.0.1:5173/blog/2023-01-02.

![4](https://blog.openreplay.com/images/creating-a-sidebar-menu-layout-with-sveltekit/images/image04.png)

## Importing components and utilities

Let's import various components from the Flowbite-Svelte library and some additional utilities such as `Cog` from `svelte-heros-v2` and the `page` from SvelteKit, `onMount`, and `sinIn` easing function from Svelte.

```javascript
<script lang="ts">
  import '../app.postcss';
  import { page } from '$app/stores';
  import { onMount } from 'svelte';
  import {
    Navbar,
    NavBrand,
    NavLi,
    NavUl,
    NavHamburger,
    Sidebar,
    SidebarGroup,
    SidebarItem,
    SidebarWrapper,
    Drawer,
    CloseButton,
    SidebarDropdownWrapper
  } from 'flowbite-svelte';
  import { Cog } from 'svelte-heros-v2';
  import { sineIn } from 'svelte/easing';
</script>
```

- Line 3: The SvelteKit `$page` store is imported, which allows the current page, parameters, current route, and status to be accessed.
- Line 4: [Svelte](https://svelte.dev/docs#run-time-svelte-onmount) `[onMount](https://svelte.dev/docs#run-time-svelte-onmount)` run as soon as the component has been mounted to the DOM. We are going to use it to check the screen width.
- Lines 5-18: Various Flowbite-Svelte components, including Navbar, Drawer, and Sidebar, are imported for use in the code.
- Line 19: An icon is imported for use in the NavBrand component.
- Line 20: The Svelte `sineIn` [easing function](https://svelte.dev/docs#run-time-svelte-easing) is imported.


## Main navigation

The top navigation bar consists of a hamburger menu for the Drawer and a full menu for larger screens. The NavUl and NavLi components are used to construct the main navigation.

```html
<script lang="ts">
  // Append the following
  // Navigation component
  let drawerHidden: boolean = false;
  const toggleDrawer = () => {
    drawerHidden = !drawerHidden;
  };
  let divClass = 'w-full md:block md:w-auto pr-8';
  let ulClass = 'flex flex-col p-4 mt-4 md:flex-row md:space-x-8 md:mt-0 md:text-lg md:font-medium';
</script>

<Navbar let:hidden let:toggle>
  <NavHamburger on:click={toggleDrawer} btnClass="ml-3 lg:hidden" />
  <NavBrand href="/" class="lg:ml-64">
    <Cog />
    <span class= "self-center whitespace-nowrap text-xl font-semibold dark:text-white pl-4">
      My Website
    </span>
  </NavBrand>
  <NavHamburger on:click={toggle} />
  <NavUl {hidden} {divClass} {ulClass}>
    <NavLi href="/">Home</NavLi>
    <NavLi href="/pages/about">About</NavLi>
    <NavLi href="https://github.com/shinokada/flowbite-sveltekit-responsive-sidebar-layout"
      >GitHub</NavLi
    >
  </NavUl>
</Navbar>

<div class="flex px-4 mx-auto w-full">
  <main class="lg:ml-72 w-full mx-auto">
    <slot />
  </main>
</div>
```

- Lines 4-7: The `toggleDrawer` function shows the Drawer component when the hamburger menu is clicked.
- Lines 8-9: Styling for the component.
- Line 12-29: The `Navbar` component is added to the code, with `let:hidden` and `let:toggle` directives. These prop values can be used in child components. The `hidden` prop is used with the `NavUl` component, and the `toggle` prop is used with the `NavHamburger` component.
- Line 13: The hamburger menu is hidden on screens larger than 1024px and has a click event (`toggleDrawer`) that allows it to show the Drawer component.
- Lines 14-19: The `NavBrand` component is added, using the imported icon as the logo.
- Line 20: The hamburger menu for screens smaller than 768px is added, with the `toggle` prop for the click event.
- Lines 21-27: The `NavUl` and `NavLi` components are used to construct the main navigation.
- Line 30-34: Adding styling to the main content.

Screen size larger than 769px:
![5](https://blog.openreplay.com/images/creating-a-sidebar-menu-layout-with-sveltekit/images/image05.png)

Screen size less than 768 px:
![6](https://blog.openreplay.com/images/creating-a-sidebar-menu-layout-with-sveltekit/images/image06.png)

Opening the main navigation when the screen size is less than 768px:
![7](https://blog.openreplay.com/images/creating-a-sidebar-menu-layout-with-sveltekit/images/image07.png)


Currently, the left hamburger menu doesn't do anything. In the next section, we are going to add the Drawer component.


<h2>Session Replay for Developers</h2> 
<p><em>
Uncover frustrations, understand bugs and fix slowdowns like never before with <strong><a href="https://github.com/openreplay/openreplay" target="_blank">OpenReplay</a></strong> — an open-source session replay suite for developers. It can be <strong>self-hosted</strong> in minutes, giving you complete control over your customer data
</em></p>
<img alt="OpenReplay" width="768" height="400" src="https://blog.openreplay.com/assets/overview_Z17Qdyg.png" class="astro-UXNKDZ4E" loading="lazy" decoding="async">
<em>Happy debugging! <a href="https://openreplay.com" target="_blank">Try using OpenReplay today.</a></em>





## Drawer component

```javascript
<script lang="ts">
  // Append the following
  // Drawer component
  let backdrop: boolean = false;
  let activateClickOutside = true;
  let breakPoint: number = 1024;
  let width: number;
  let transitionParams = {
    x: -320,
    duration: 200,
    easing: sineIn
  };
  $: if (width >= breakPoint) {
    drawerHidden = false;
    activateClickOutside = false;
  } else {
    drawerHidden = true;
    activateClickOutside = true;
  }
  onMount(() => {
    if (width >= breakPoint) {
      drawerHidden = false;
      activateClickOutside = false;
    } else {
      drawerHidden = true;
      activateClickOutside = true;
    }
  });
</script>

<svelte:window bind:innerWidth={width} />
<!-- After Navbar -->
<Drawer
  transitionType= "fly"
  {backdrop}
  {transitionParams}
  bind:hidden={drawerHidden}
  bind:activateClickOutside
  width="w-64"
  class="overflow-scroll pb-32"
  id= "sidebar"
>
  Drawer
</Drawer>
```

- Line 4: The backdrop for the Drawer component is set to `false`. The default value is `true`, so if you want to show the backdrop, remove it or set it to `true`.
- Line 5: The `activateClickOutside` prop is set to `true`, allowing the Drawer component to be closed by clicking outside its area.
- Line 6: The breakpoint for the screen size is set to 1024px.
- Line 7: A `width` variable is created to store the screen width.
- Line 8-12: Transition parameters are set.
- Line 13-19: The `drawerHidden` and `activateClickOutside` props are controlled based on the screen size. If the screen size is less than the breakpoint (1024px), they are set to `false`; otherwise, they are set to `true`.
- Line 20-28: The same actions are performed on the component mount, checking the screen size at that time.
- Line 31: The `<svelte:window>` element is used to add event listeners for changes in the window size, and the `innerWidth` is bound to the `width` variable.
- Line 33-44: We add the Drawer component using the `transitionType` property to "fly" and pass values to the `backdrop` and `transitionParams` properties. We also bind the `hidden` property to a value `drawerHidden` and set the `activateClickOutside` property. The `width` property is set to `w-64`.

Make the screen size less than 1024px and click the hamburger menu to see the Drawer component in action.

![8](https://blog.openreplay.com/images/creating-a-sidebar-menu-layout-with-sveltekit/images/image08.png)



In the next section, we will add the Sidebar Component within the Drawer component.

## Sidebar

```html
<script lang="ts">
// Append the following
// Sidebar Component
const toggleSide = () => {
  if (width < breakPoint) {
    drawerHidden = !drawerHidden;
  }
};
$: activeUrl = $page.url.pathname;
let spanClass = 'pl-2 self-center text-md text-gray-900 whitespace-nowrap dark:text-white';
</script> 

<!-- Add the following within the Drawer component -->
<div class=" flex items-center">
  <CloseButton on:click={() => (drawerHidden = true)} class="mb-4 dark:text-white lg:hidden" />
</div>
<Sidebar asideClass="w-54">
  <SidebarWrapper divClass="overflow-y-auto py-4 px-3 rounded dark:bg-gray-800">
    <SidebarGroup>
      <SidebarItem label="Home" href="/" on:click={toggleSide} active={activeUrl === `/`} />
      <SidebarItem
        label= "About"
        href="/pages/about"
        {spanClass}
        on:click={toggleSide}
        active={activeUrl === '/pages/about'}
      />
      <SidebarDropdownWrapper label="Articles">
        <SidebarItem
          label= "Article 1"
          href={`/blog/2023-01-01`}
          {spanClass}
          on:click={toggleSide}
          active={activeUrl === '/blog/2023-01-01'}
        />
        <SidebarItem
          label= "Article 2"
          href="/blog/2023-01-02"
          {spanClass}
          on:click={toggleSide}
          active={activeUrl === '/blog/2023-01-02'}
        />
      </SidebarDropdownWrapper>
    </SidebarGroup>
  </SidebarWrapper>
</Sidebar>
```

- Line 4-8: `toggleSide` is a function that toggles the value of `drawerHidden` if the screen's width is less than the `breakPoint` value.
- Line 9: `activeUrl` is a reactive variable set to the current page's URL pathname.
- Line 10: `spanClass` contains a class for styling a `SidebarItem` components.

Check it on your localhost:

```
npm run dev
```

![9](https://blog.openreplay.com/images/creating-a-sidebar-menu-layout-with-sveltekit/images/image09.png)



## Adding icons to sidebar items

You can enhance the appearance and usability of your sidebar menu by adding icons to its items. For instance, you can add a home icon next to the 'Home' menu item to make it more visually appealing and easier to identify for users.

```html
<script>
import { Home } from 'svelte-heros-v2'
</script>

<SidebarItem label="Home" href="/" on:click={toggleSide} active={activeUrl === `/`}>
  <svelte:fragment slot="icon">
    <Home />
  </svelte:fragment>
<SidebarItem />
```

Remember to include the necessary import statement at the top of your script tag: `import { Home } from 'svelte-heros-v2". Then, you can use the `icon` slot to add your icon component within the menu item. This will ensure that the icon is displayed properly in your sidebar menu.


## Conclusion

This tutorial provided in this article demonstrates how to use SvelteKit and Flowbite-Svelte to create a responsive sidebar menu layout for a website. These tools allow for the creation of a professional-looking menu with minimal effort and time. By following the steps outlined in the tutorial, you can easily set up and customize your own responsive sidebar menu layout using SvelteKit and Flowbite-Svelte.
There may be additional tasks to complete before your site is fully functional, but many of them can be efficiently addressed by utilizing the Flowbite-Svelte components. These components provide pre-built, customizable elements that can save time and simplify development.


- Adding a [dark mode](https://flowbite-svelte.com/components/darkmode)
- Adding a [breadcrumb component](https://flowbite-svelte.com/components/breadcrumb)
- Adding a [footer component](https://flowbite-svelte.com/components/footer)
- More components can be found from https://flowbite-svelte.com/

Additional details can be found in the [repository](https://github.com/shinokada/responsive-sidebar-layout) and demo at https://sveltekit-sidebar-layout.vercel.app/.


> A TIP FROM THE EDITOR: If you are interested in Svelte, don't miss [Exploring SvelteKit In 2022 By Building A Portfolio Website](https://blog.openreplay.com/exploring-sveltekit-in-2022-by-building-a-portfolio-website/).



[![newsletter](https://blog.openreplay.com/newsletter_Z1P0UIP.png)](https://newsletter.openreplay.com/) 



```