Creating a sidebar menu in Svelte
Tags: svelte, javascript, css
Published: 2020-02-29 08:53:28

---
title: Creating a sidebar menu in Svelte
published: true
description: Constructing a sidebar menu using Svelte and Tailwind
tags: svelte, javascript, css
cover_image: https://dev-to-uploads.s3.amazonaws.com/i/9176ka86pag3u7ugzoc8.gif
---

A common requirement for web apps is a sidebar menu, at least on mobile, sometimes on desktop too.

Here's how you can roll your own with Svelte:

# Component Tree

First, let's think 🤔 about what kind of components we need in our tree:

- Navigation bar `<Navbar/>` for our header
  - Logo `<Logo/>` with a clickable `<svg>` 
  - Menu `<Menu/>` with clickable links 
  - Icon `<Hamburger/>` to trigger the sidebar  
- Sidebar `<Sidebar/>` that will float above the page
- Main area `</Main>` where we can put the page content

## Page Layout

Our top level layout will be in `App.svelte`. We'll define a boolean flag `open` to track when the sidebar is open.

```html
<!-- App.svelte -->
<script>
  import Navbar from './Navbar.svelte'
  import Sidebar from './Sidebar.svelte'
  import Main from './Main.svelte'

  let open = false
</script>

<Navbar bind:sidebar={open}/>
<Sidebar bind:open/>
<Main/>
```

Let's also include [Tailwind](https::/tailwindcss.com) and some global styles. If you're using [Svlete's REPL](https://svelte.dev/repl) it can be added with `<svelte:head>`. If you're using `rollup` or `webpack`, obvs. use the `npm` package instead.

```html
<svelte:head>
  <link href="https://unpkg.com/tailwindcss@^1.0/dist/tailwind.min.css" rel="stylesheet"/>
</svelte:head>

<style>
  :global(body) {
    padding: 0;
  }
</style>
```

# Navigation bar

In our navigation, we'll use Tailwind's `justify-between` to keep the hamburger and logo on the left, with the menu on the right.

```html
<!-- Navbar.svelte -->
<script>
  import Logo from './Logo.svelte'
  import Hamburger from './Hamburger.svelte'
  import Menu from './Menu.svelte'

  export let sidebar = false
</script>

<header class="flex justify-between bg-gray-200 p-2 items-center text-gray-600 border-b-2">

  <!-- left side -->
  <nav class="flex">
    <Hamburger bind:open={sidebar}/>
    <Logo/>	
  </nav>
	
  <!-- right side -->
  <Menu/>
</header>
```

## Logo

The logo component is just a simple wrapper around `<svg>`

```html
<!-- Logo.svelte -->
<a href="/"><svg> .... </svg></a>
```

Not too much to see here 🙈

## Hamburger Icon

The hamburger component is also an `<svg>`, but it has a CSS transition that toggles from a 3 line "hamburger" to a 2-line "X" when the menu bar is open.

```html
<!-- Hamburger.svelte -->
<script>
  export let open = false
</script>

<!-- defines a CSS class `.open` when `open == true` -->
<button class:open on:click={() => open = !open}>
  <!-- svg with 3 lines -->
  <svg width=32 height=24>
   <line id="top" x1=0 y1=2 x2=32 y2=2/>
   <line id="middle" x1=0 y1=12 x2=24 y2=12/>
   <line id="bottom" x1=0 y1=22 x2=32 y2=22/>
  </svg>
</button>
```

Then we define some CSS:

```css
svg {
  min-height: 24px;
  transition: transform 0.3s ease-in-out;
}

svg line {
  /* `currentColor` means inherit color from the text color */
  stroke: currentColor;
  stroke-width: 3;
  transition: transform 0.3s ease-in-out
}

/* adjust the Z-index, so that the icon is on top of the sidebar */
button {
  z-index: 20;
}

.open svg {
  transform: scale(0.7)
}

/* rotate the top line */
.open #top {
  transform: translate(6px, 0px) rotate(45deg)
}

/* hide the middle */
.open #middle {
  opacity: 0;
}

/* rotate the bottom line */
.open #bottom {
  transform: translate(-12px, 9px) rotate(-45deg)
}
```

# Floating Sidebar

The sidebar component is an `<aside>` that is offscreen by default `left: -100%`, but when `open == true`, the class `.open` is added, which transitions the sidebar to `left: 0`. That makes it slide across the screen.

```html
<script>
  export let open = false
</script>

<aside class="absolute w-full h-full bg-gray-200 border-r-2 shadow-lg sm:hidden" class:open>
  <nav class="p-12 text-xl">
    <a class="block" href="#about">About</a>
    <a class="block" href="#contact">Contact</a>
  </nav>
</aside>

<style>
  aside {
    /* offscreen by default */
    left: -100%;
    transition: left 0.3s ease-in-out
  }
  
  .open {
    /* slide on screen */
    left: 0
  }
</style>
```

# Conclusion

So there you have it folks, it's that easy!

A useful addition would be to define the menu tree as a JavaScript object, instead of hardcoding it in both the sidebar and navbar menus.

You can find a [fully working example here](https://svelte.dev/repl/03f0be0c4dc54eb4af5a168f644f5c31?version=3.19.1) 

Happy hacking ✌


# Want more?

If you'd like to learn more about Svelte, check out my [short video courses](https://joshuanussbaum.podia.com) ✨