Routing & links

How the framework-agnostic library integrates with your router.

The core @adminlte/vue library never imports a router. Instead, LteDashboardLayout takes a small set of props that let you wire it to whatever routing solution your app uses — Nuxt, Vue Router, or plain anchors. This keeps the library framework-agnostic while still giving you active-link highlighting and working navigation in the command palette.

The three integration points

PropTypeDefaultWhat it does
currentPathstring'/'The current route path. Used for active-link detection in the sidebar — the nav item whose href matches gets the active state.
linkComponentstring | Component'a'The component used to render sidebar and palette links. Pass your router's link component (e.g. NuxtLink) for client-side navigation.
navigate(href: string) => voidCallback invoked when a command-palette result is chosen or a sidebar link is clicked. Wire it to your router's programmatic navigation.

By default the layout renders plain <a> tags and reads currentPath as '/', so it works with zero configuration — links just do full-page navigation.

linkComponent or navigate?

Either one gives you client-side navigation in the sidebar — pick whichever fits your app:

  • linkComponent renders your router's own link component, so you get its prefetching, aria-current handling and real hrefs for free. Prefer it when you have one.
  • navigate keeps the plain <a> markup and intercepts clicks, calling your callback with the item's href. Useful when you have no link component (a custom router, an embedded app, or navigation that runs through your own logic — analytics, unsaved-changes guards).

Passing both is safe: the link component handles the click first, and the sidebar stands down rather than navigating twice.

The interception is deliberately conservative — the browser keeps handling anything that isn't a plain in-app navigation:

ClickHandled by
Plain left click on an in-app hrefnavigate
Ctrl / ⌘ / Shift / Alt click, middle clickBrowser (new tab / window)
target: '_blank'Browser
Absolute URL (https://…), protocol-relative (//…), mailto:Browser
Placeholder / hash-only href ('#', '#section')Browser

Nuxt example

In Nuxt, pull the current path from useRoute(), resolve NuxtLink for client-side navigation, and pass navigateTo as the palette's navigate callback.

<script setup lang="ts">
import { resolveComponent } from 'vue'
import type { MenuNode } from '@adminlte/vue'

const route = useRoute()
const NuxtLink = resolveComponent('NuxtLink')

const menuItems: MenuNode[] = [
  { type: 'header', text: 'MAIN' },
  { type: 'item', text: 'Dashboard', href: '/', icon: 'bi-speedometer' },
  {
    type: 'group',
    text: 'Pages',
    icon: 'bi-files',
    children: [
      { type: 'item', text: 'Profile', href: '/pages/profile' },
      { type: 'item', text: 'Settings', href: '/pages/settings' },
    ],
  },
]
</script>

<template>
  <LteDashboardLayout
    :menu-items="menuItems"
    :current-path="route.path"
    :link-component="NuxtLink"
    :navigate="navigateTo"
  >
    <slot />
  </LteDashboardLayout>
</template>

The @adminlte/nuxt module auto-registers LteDashboardLayout and auto-imports the MenuNode type, so no manual component import is needed in a Nuxt app.

Vue Router example

The same props work with plain Vue Router — resolve RouterLink, read the path from the current route, and use router.push for the palette.

<script setup lang="ts">
import { resolveComponent } from 'vue'
import { useRoute, useRouter } from 'vue-router'
import { LteDashboardLayout, type MenuNode } from '@adminlte/vue'

const route = useRoute()
const router = useRouter()
const RouterLink = resolveComponent('RouterLink')

const menuItems: MenuNode[] = [
  { type: 'item', text: 'Dashboard', href: '/', icon: 'bi-speedometer' },
]
</script>

<template>
  <LteDashboardLayout
    :menu-items="menuItems"
    :current-path="route.path"
    :link-component="RouterLink"
    :navigate="(href) => router.push(href)"
  >
    <RouterView />
  </LteDashboardLayout>
</template>

Most router link components (NuxtLink, RouterLink) accept an href/to prop and render an anchor, so the sidebar's link rendering stays compatible. With the default 'a', links navigate with a full page load.

The menu structure

A single MenuNode[] array drives both the sidebar navigation tree and the ⌘K command palette. MenuNode is a discriminated union of three node types, distinguished by the type field.

A non-interactive section label.

FieldTypeDescription
type'header'Discriminant.
textstringThe label text.

A clickable leaf link.

FieldTypeDescription
type'item'Discriminant.
textstringLink label.
hrefstringLink target. Matched against currentPath for the active state.
iconstringBootstrap Icons class, e.g. 'bi-speedometer' (with or without the leading bi).
iconColorBootstrapThemeIcon color variant.
badgestring | numberOptional badge content.
badgeColorBootstrapThemeBadge color variant.
target'_blank' | '_self'Anchor target.
attrsRecord<string, string | number | boolean>Extra attributes spread onto the rendered link — data-* hooks for tests/analytics, aria-*, rel, title. The resolved link target always wins, so attrs cannot overwrite href / to.

A collapsible submenu (treeview) that nests further MenuNodes.

FieldTypeDescription
type'group'Discriminant.
textstringGroup label.
iconstringBootstrap Icons class.
iconColorBootstrapThemeIcon color variant.
badgestring | numberOptional badge content.
badgeColorBootstrapThemeBadge color variant.
childrenMenuNode[]Nested nodes — groups can nest other groups for multi-level menus.
attrsRecord<string, string | number | boolean>Extra attributes spread onto the group's toggle <button>.
import type { MenuNode } from '@adminlte/vue'

const menuItems: MenuNode[] = [
  { type: 'header', text: 'NAVIGATION' },
  { type: 'item', text: 'Dashboard', href: '/', icon: 'bi-speedometer' },
  {
    type: 'group',
    text: 'Pages',
    icon: 'bi-files',
    badge: 4,
    badgeColor: 'secondary',
    children: [
      {
        type: 'item',
        text: 'Profile',
        href: '/pages/profile',
        attrs: { 'data-testid': 'nav-profile' },
      },
      {
        type: 'group',
        text: 'Auth',
        children: [
          { type: 'item', text: 'Login', href: '/login' },
          { type: 'item', text: 'Register', href: '/register' },
        ],
      },
    ],
  },
]

The sidebar compares each MenuItem's href against currentPath to apply the active class. Because currentPath is reactive, the highlighted item updates automatically on navigation when you wire it to route.path.

For nested menus, set accordion on the layout to keep only one group open at a time:

<LteDashboardLayout :menu-items="menuItems" :current-path="route.path" :accordion="true">
  <slot />
</LteDashboardLayout>