Side Navigation
A component that allows users to navigate through a side navigation menu.
<!-- Side Navigation -->
<!-- An Alpine.js and Tailwind CSS component by https://pinemix.com -->
<div
x-data="{
// If set to true, only one item can be expanded at a time
toggleExclusive: true,
// Navigation Items
items: [
{
label: 'Dashboard',
href: null,
icon: '<svg xmlns=\'http://www.w3.org/2000/svg\' viewBox=\'0 0 20 20\' fill=\'currentColor\' class=\'hi-mini hi-home inline-block size-5\'><path fill-rule=\'evenodd\' d=\'M9.293 2.293a1 1 0 0 1 1.414 0l7 7A1 1 0 0 1 17 11h-1v6a1 1 0 0 1-1 1h-2a1 1 0 0 1-1-1v-3a1 1 0 0 0-1-1H9a1 1 0 0 0-1 1v3a1 1 0 0 1-1 1H5a1 1 0 0 1-1-1v-6H3a1 1 0 0 1-.707-1.707l7-7Z\' clip-rule=\'evenodd\'/></svg>',
badge: null
},
{
label: 'Analytics',
href: null,
icon: '<svg xmlns=\'http://www.w3.org/2000/svg\' viewBox=\'0 0 20 20\' fill=\'currentColor\' class=\'hi-mini hi-chart-bar inline-block size-5\'><path d=\'M15.5 2A1.5 1.5 0 0 0 14 3.5v13a1.5 1.5 0 0 0 1.5 1.5h1a1.5 1.5 0 0 0 1.5-1.5v-13A1.5 1.5 0 0 0 16.5 2h-1ZM9.5 6A1.5 1.5 0 0 0 8 7.5v9A1.5 1.5 0 0 0 9.5 18h1a1.5 1.5 0 0 0 1.5-1.5v-9A1.5 1.5 0 0 0 10.5 6h-1ZM3.5 10A1.5 1.5 0 0 0 2 11.5v5A1.5 1.5 0 0 0 3.5 18h1A1.5 1.5 0 0 0 6 16.5v-5A1.5 1.5 0 0 0 4.5 10h-1Z\'/></svg>',
badge: 'New'
},
{
label: 'Projects',
href: null,
icon: '<svg xmlns=\'http://www.w3.org/2000/svg\' viewBox=\'0 0 20 20\' fill=\'currentColor\' class=\'hi-mini hi-briefcase inline-block size-5\'><path fill-rule=\'evenodd\' d=\'M6 3.75A2.75 2.75 0 0 1 8.75 1h2.5A2.75 2.75 0 0 1 14 3.75v.443c.572.055 1.14.122 1.706.2C17.053 4.582 18 5.75 18 7.07v3.469c0 1.126-.694 2.191-1.83 2.54-1.952.599-4.024.921-6.17.921s-4.219-.322-6.17-.921C2.694 12.73 2 11.665 2 10.539V7.07c0-1.321.947-2.489 2.294-2.676A41.047 41.047 0 0 1 6 4.193V3.75Zm6.5 0v.325a41.622 41.622 0 0 0-5 0V3.75c0-.69.56-1.25 1.25-1.25h2.5c.69 0 1.25.56 1.25 1.25ZM10 10a1 1 0 0 0-1 1v.01a1 1 0 0 0 1 1h.01a1 1 0 0 0 1-1V11a1 1 0 0 0-1-1H10Z\' clip-rule=\'evenodd\'/><path d=\'M3 15.055v-.684c.126.053.255.1.39.142 2.092.642 4.313.987 6.61.987 2.297 0 4.518-.345 6.61-.987.135-.041.264-.089.39-.142v.684c0 1.347-.985 2.53-2.363 2.686a41.454 41.454 0 0 1-9.274 0C3.985 17.585 3 16.402 3 15.055Z\'/></svg>',
badge: null,
expanded: false,
children: [
{ label: 'Active', href: null, badge: null },
{ label: 'Completed', href: null, badge: null },
{ label: 'Archived', href: null, badge: null }
]
},
{
label: 'Team',
href: null,
icon: '<svg xmlns=\'http://www.w3.org/2000/svg\' viewBox=\'0 0 20 20\' fill=\'currentColor\' class=\'hi-mini hi-users inline-block size-5\'><path d=\'M7 8a3 3 0 1 0 0-6 3 3 0 0 0 0 6ZM14.5 9a2.5 2.5 0 1 0 0-5 2.5 2.5 0 0 0 0 5ZM1.615 16.428a1.224 1.224 0 0 1-.569-1.175 6.002 6.002 0 0 1 11.908 0c.058.467-.172.92-.57 1.174A9.953 9.953 0 0 1 7 18a9.953 9.953 0 0 1-5.385-1.572ZM14.5 16h-.106c.07-.297.088-.611.048-.933a7.47 7.47 0 0 0-1.588-3.755 4.502 4.502 0 0 1 5.874 2.636.818.818 0 0 1-.36.98A7.465 7.465 0 0 1 14.5 16Z\'/></svg>',
badge: null,
expanded: false,
children: [
{ label: 'Members', href: null, badge: null },
{ label: 'Roles', href: null, badge: null },
{ label: 'Permissions', href: null, badge: null }
]
},
{
label: 'Reports',
href: null,
icon: '<svg xmlns=\'http://www.w3.org/2000/svg\' viewBox=\'0 0 20 20\' fill=\'currentColor\' class=\'hi-mini hi-document-text inline-block size-5\'><path fill-rule=\'evenodd\' d=\'M4.5 2A1.5 1.5 0 0 0 3 3.5v13A1.5 1.5 0 0 0 4.5 18h11a1.5 1.5 0 0 0 1.5-1.5V7.621a1.5 1.5 0 0 0-.44-1.06l-4.12-4.122A1.5 1.5 0 0 0 11.378 2H4.5Zm2.25 8.5a.75.75 0 0 0 0 1.5h6.5a.75.75 0 0 0 0-1.5h-6.5Zm0 3a.75.75 0 0 0 0 1.5h6.5a.75.75 0 0 0 0-1.5h-6.5Z\' clip-rule=\'evenodd\'/></svg>',
badge: null,
expanded: false,
children: [
{ label: 'Sales Report', href: null, badge: null },
{ label: 'User Analytics', href: null, badge: null },
{ label: 'Performance', href: null, badge: null }
]
},
{
label: 'Settings',
href: null,
icon: '<svg xmlns=\'http://www.w3.org/2000/svg\' viewBox=\'0 0 20 20\' fill=\'currentColor\' class=\'hi-mini hi-cog-8-tooth inline-block size-5\'><path fill-rule=\'evenodd\' d=\'M8.34 1.804A1 1 0 0 1 9.32 1h1.36a1 1 0 0 1 .98.804l.295 1.473c.497.144.971.342 1.416.587l1.25-.834a1 1 0 0 1 1.262.125l.962.962a1 1 0 0 1 .125 1.262l-.834 1.25c.245.445.443.919.587 1.416l1.473.294a1 1 0 0 1 .804.98v1.361a1 1 0 0 1-.804.98l-1.473.295a6.95 6.95 0 0 1-.587 1.416l.834 1.25a1 1 0 0 1-.125 1.262l-.962.962a1 1 0 0 1-1.262.125l-1.25-.834a6.953 6.953 0 0 1-1.416.587l-.294 1.473a1 1 0 0 1-.98.804H9.32a1 1 0 0 1-.98-.804l-.295-1.473a6.957 6.957 0 0 1-1.416-.587l-1.25.834a1 1 0 0 1-1.262-.125l-.962-.962a1 1 0 0 1-.125-1.262l.834-1.25a6.957 6.957 0 0 1-.587-1.416l-1.473-.294A1 1 0 0 1 1 10.68V9.32a1 1 0 0 1 .804-.98l1.473-.295c.144-.497.342-.971.587-1.416l-.834-1.25a1 1 0 0 1 .125-1.262l.962-.962A1 1 0 0 1 5.38 3.03l1.25.834a6.957 6.957 0 0 1 1.416-.587l.294-1.473ZM13 10a3 3 0 1 1-6 0 3 3 0 0 1 6 0Z\' clip-rule=\'evenodd\'/></svg>',
badge: null
}
],
// Initialize - expand submenus with active children based on href
init() {
this.items.forEach(item => {
if (item.children && this.hasActiveChild(item)) {
item.expanded = true;
}
});
},
// Toggle expanded state for parent items
toggleExpanded(item) {
if (item && item.children) {
// If toggle exclusive is enabled, close all other items first
if (this.toggleExclusive) {
this.items.forEach(i => {
if (i !== item && i.children) {
i.expanded = false;
}
});
}
item.expanded = !item.expanded;
}
},
// Check if item is active
isActive(href) {
return href && window.location.pathname === href;
},
// Check if parent has active child
hasActiveChild(item) {
if (!item.children) return false;
return item.children.some(child => this.isActive(child.href));
},
}"
class="mx-auto w-64"
>
<!-- Side Navigation Container -->
<nav
class="flex flex-col gap-1 rounded-xl border border-zinc-200 bg-white p-4 ring-3 ring-zinc-200/50 dark:border-zinc-700 dark:bg-zinc-800 dark:ring-zinc-800"
>
<template x-for="item in items" :key="item.label">
<div>
<!-- Link Item -->
<a
x-show="!item.children"
x-bind:href="item.href || 'javascript:void(0)'"
x-bind:class="{
'text-zinc-800 hover:bg-zinc-50 hover:text-zinc-950 dark:text-zinc-200 dark:hover:bg-zinc-700/60 dark:hover:text-white': !isActive(item.href),
'bg-zinc-100 text-zinc-950 dark:bg-zinc-700/60 dark:text-white': isActive(item.href)
}"
class="group flex items-center gap-3 rounded-xl px-3 py-2 text-sm font-medium"
>
<!-- Icon -->
<div
x-show="item.icon"
x-html="item.icon"
class="flex-none opacity-40 group-hover:opacity-80"
></div>
<!-- Label -->
<span x-text="item.label" class="truncate"></span>
<!-- Badge -->
<span
x-show="item.badge"
x-text="item.badge"
x-bind:class="{
'bg-zinc-200 text-zinc-800 dark:bg-zinc-600 dark:text-zinc-200': !isActive(item.href),
'bg-zinc-800 text-white dark:bg-zinc-200 dark:text-zinc-800': isActive(item.href)
}"
class="ms-auto inline-flex items-center rounded-full px-1.75 py-0.5 text-xs font-medium"
></span>
<!-- END Badge -->
</a>
<!-- END Link Item -->
<!-- Submenu Button -->
<button
x-show="item.children"
x-on:click="toggleExpanded(item)"
x-bind:class="{
'text-zinc-800 hover:bg-zinc-50 hover:text-zinc-950 dark:text-zinc-200 dark:hover:bg-zinc-700/60 dark:hover:text-white': !hasActiveChild(item),
'bg-zinc-100 text-zinc-950 dark:bg-zinc-700/60 dark:text-white': hasActiveChild(item)
}"
class="group flex w-full items-center justify-between rounded-xl px-3 py-2 text-sm font-medium"
>
<div class="flex flex-1 items-center gap-3">
<!-- Icon -->
<div
x-show="item.icon"
x-html="item.icon"
class="flex-none opacity-40 group-hover:opacity-80"
></div>
<!-- Label -->
<span x-text="item.label" class="truncate"></span>
<!-- Badge -->
<span
x-show="item.badge"
x-text="item.badge"
x-bind:class="{
'bg-zinc-200 text-zinc-800 dark:bg-zinc-600 dark:text-zinc-200': !hasActiveChild(item),
'bg-zinc-800 text-white dark:bg-zinc-200 dark:text-zinc-800': hasActiveChild(item)
}"
class="ms-auto inline-flex items-center rounded-full px-1.75 py-0.5 text-xs font-medium"
></span>
</div>
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 20 20"
fill="currentColor"
class="hi-mini hi-chevron-down inline-block size-5 opacity-40"
x-bind:class="{ 'rotate-180': item.expanded }"
>
<path
fill-rule="evenodd"
d="M5.22 8.22a.75.75 0 0 1 1.06 0L10 11.94l3.72-3.72a.75.75 0 1 1 1.06 1.06l-4.25 4.25a.75.75 0 0 1-1.06 0L5.22 9.28a.75.75 0 0 1 0-1.06Z"
clip-rule="evenodd"
/>
</svg>
</button>
<!-- END Submenu Button -->
<!-- Submenu Items -->
<div
x-show="item.children && item.expanded"
class="ms-8 mt-1 flex flex-col gap-0.5"
>
<template x-for="child in item.children" :key="child.label">
<!-- Link Item -->
<a
x-bind:href="child.href || 'javascript:void(0)'"
x-bind:class="{
'text-zinc-700 hover:bg-zinc-50 hover:text-zinc-900 dark:text-zinc-300 dark:hover:bg-zinc-700/60 dark:hover:text-white': !isActive(child.href),
'bg-zinc-100 text-zinc-900 dark:bg-zinc-700/60 dark:text-white': isActive(child.href)
}"
class="group flex items-center justify-between rounded-md px-3 py-1.25 text-sm"
>
<!-- Label -->
<span x-text="child.label" class="truncate"></span>
<!-- Badge -->
<span
x-show="child.badge"
x-text="child.badge"
x-bind:class="{
'bg-zinc-200 text-zinc-800 dark:bg-zinc-600 dark:text-zinc-200': !isActive(child.href),
'bg-zinc-800 text-white dark:bg-zinc-200 dark:text-zinc-800': isActive(child.href)
}"
class="ms-auto inline-flex items-center rounded-full px-1.75 py-0.5 text-xs font-medium"
></span>
<!-- END Badge -->
</a>
<!-- END Link Item -->
</template>
</div>
<!-- END Submenu Items -->
</div>
</template>
</nav>
<!-- END Side Navigation Container -->
</div>
<!-- END Side Navigation -->
Props
The available data properties for this component.
Property | Default | Description |
---|---|---|
toggleExclusive | true | If set to 'true', only one item can be expanded at a time |
items | [] | An object array to populate the available items in the side navigation menu. Available item values are 'label', 'icon', 'href' and 'badge'. |
Designed with
Tailkit
Featuring 2,000+ Tailwind CSS code snippets for HTML, React, Vue.js and Alpine.js projects
Unlock 15+ free templates
Join our pixelcave newsletter to get them now & we'll also keep you updated about any new Pinemix components!