DEV Community

Cover image for Creating a sidebar menu in Svelte

Creating a sidebar menu in Svelte

joshnuss profile image Joshua Nussbaum ・3 min read

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.

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

  let open = false

<Navbar bind:sidebar={open}/>
<Sidebar bind:open/>
Enter fullscreen mode Exit fullscreen mode

Let's also include Tailwind and some global styles. If you're using Svlete's REPL it can be added with <svelte:head>. If you're using rollup or webpack, obvs. use the npm package instead.

  <link href="^1.0/dist/tailwind.min.css" rel="stylesheet"/>

  :global(body) {
    padding: 0;
Enter fullscreen mode Exit fullscreen mode

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.

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

  export let sidebar = false

<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}/>

  <!-- right side -->
Enter fullscreen mode Exit fullscreen mode


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

<!-- Logo.svelte -->
<a href="/"><svg> .... </svg></a>
Enter fullscreen mode Exit fullscreen mode

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.

<!-- Hamburger.svelte -->
  export let open = false

<!-- 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/>
Enter fullscreen mode Exit fullscreen mode

Then we define some 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)
Enter fullscreen mode Exit fullscreen mode

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.

  export let open = false

<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>

  aside {
    /* offscreen by default */
    left: -100%;
    transition: left 0.3s ease-in-out

  .open {
    /* slide on screen */
    left: 0
Enter fullscreen mode Exit fullscreen mode


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

Happy hacking ✌

If you want to learn more about Svelte, check out my upcoming video course

Discussion (3)

Editor guide
rhaddlesey profile image
Dr Richard Haddlesey

How do we then add a submenu? Say, I hover over contact and I want a sub-menu with - phone, email, address etc to fold out from contact?


joshnuss profile image
Joshua Nussbaum Author

For the Sidebar, you can nest multiple levels of <nav>. The menu items that have submenus should use <button> instead of <a>. Then when the user clicks the <button>, toggle the visibility of the child <nav>.

For the Menu component, you can use a similar approach, except instead of clicking to show the submenus, you can can use CSS :hover modifier to show the submenu. Also, the submenu will need position: absolute so the it floats above