SideNav
The mobile menu: a left USlideover holding the user header, nav items, a light/dark toggle and a sign-out row. Its open state lives in useMobileNav() rather than in a prop, so the app's hamburger and this drawer stay in sync.
vue
<script setup lang="ts">
import RyderSideNav from "@opeolluwa/ryder/components/navigation/SideNav.vue"
</script>
<template>
<RyderSideNav :items="items" :user="user" @logout="logout" />
</template>Examples
vue
<script setup lang="ts">
import { inject, provide, ref } from "vue";
import type { RouteLocationNormalizedLoaded } from "vue-router";
import { routeLocationKey } from "vue-router";
import SideNav from "../../../src/components/navigation/SideNav.vue";
import { useMobileNav } from "../../../src/composables/useMobileNav";
// Same `#imports -> vue-router` note as the BottomNav demo: the docs host has
// no router, so give the active-item check a path when nothing else has.
if (!inject(routeLocationKey, null)) {
provide(routeLocationKey, { path: "/" } as RouteLocationNormalizedLoaded);
}
const { mobileNavOpen, openMobileNav } = useMobileNav();
const items = [
{ label: "Home", to: "/", icon: "heroicons:home" },
{ label: "Cart", icon: "heroicons:shopping-cart", action: "toggle-cart", badge: 3 },
{ label: "Settings", icon: "heroicons:cog-6-tooth", action: "open-settings" },
];
const user = {
name: "Docs demo",
description: "Signed in",
avatar: { text: "DD", alt: "Docs demo" },
};
const lastEvent = ref("");
</script>
<template>
<div class="space-y-3">
<div class="flex flex-wrap items-center gap-3">
<button
type="button"
class="rounded-lg bg-primary-600 px-4 py-2 text-sm font-medium text-white hover:bg-primary-700"
@click="openMobileNav()"
>
Open side nav
</button>
<span class="text-sm text-gray-500 dark:text-white/50">
{{ lastEvent || `Drawer is ${mobileNavOpen ? "open" : "closed"}` }}
</span>
</div>
<SideNav
:items="items"
:user="user"
root-path="/"
@action="lastEvent = `action: ${$event}`"
@logout="lastEvent = 'logout'"
/>
</div>
</template>Props
| Prop | Type | Default | Description |
|---|---|---|---|
items | SideNavItem[] | [] | Menu entries: { to?, label, icon, badge?, action? }. |
user | SideNavUser | {} | Drawer header identity: { name?, description?, avatar?: { src?, text?, alt?, icon? } }. Missing name falls back to "User". |
rootPath | string | "/" | Path treated as "always active" when it is the current route. |
SideNavItem fields:
| Field | Type | Description |
|---|---|---|
to | string? | Navigation target; omit for an action item. |
label | string | Entry label (and fallback key). |
icon | string | Icon name. |
badge | string | number | null? | Trailing count pill (unread count, cart items, …). |
action | string? | Non-navigation entry — closes the drawer and emits action with this name. |
Emits
| Event | Payload | When |
|---|---|---|
action | name: string | An action item was tapped — item.action ?? item.label, after the drawer closes. |
logout | — | The built-in "Sign out" row was tapped — the app performs its own teardown. |
Slots
None.
Notes
- Open state: there is no
openprop. The drawer bindsv-model:open="mobileNavOpen"internally, so the caller opens it through the same composable —useMobileNav()returnsmobileNavOpen,openMobileNav(),closeMobileNav()andtoggleMobileNav()(backed by Nuxt'suseState, so any component in the app sees the same flag). - Usually slotted into
RyderShell'smobile-navslot; import it explicitly from app layout files (Nuxt does not auto-import components there). - Nuxt-only imports:
useColorModeanduseRoutefrom#imports, plusNuxtLinkfortoitems; Nuxt UI providesUSlideover,UUser,UButtonandUSeparator. The docs demo stubs vue-router's route key for the same reason as the BottomNav example (the docs host has no router). - The drawer header includes a built-in light/dark toggle that writes
colorMode.preference. rootPathonly affects active highlighting; items withouttorender as buttons so a missing route is never requested.