Frappe UIFrappe UI

MobileNav

The bottom tab bar for a MobileShell. MobileNav is a grid frame; each MobileNavItem becomes one equal-width tab, so the bar adapts to any number of items.

vue
<script setup lang="ts">
import { ref } from 'vue'
import { MobileNav, MobileNavItem, Avatar } from 'frappe-ui'

const tab = ref('home')
</script>

<template>
  <div
    class="mx-auto w-[360px] overflow-hidden border-b border-l border-r bg-surface-white"
  >
    <MobileNav>
      <MobileNavItem
        label="Home"
        icon="lucide-house"
        :active="tab === 'home'"
        @click="tab = 'home'"
      />
      <MobileNavItem
        label="Notifications"
        icon="lucide-bell"
        :active="tab === 'notifications'"
        @click="tab = 'notifications'"
      />
      <MobileNavItem
        label="Search"
        icon="lucide-search"
        :active="tab === 'search'"
        @click="tab = 'search'"
      />
      <MobileNavItem label="You" :active="tab === 'you'" @click="tab = 'you'">
        <template #default="{ active }">
          <Avatar
            label="Jane Doe"
            size="md"
            :class="active ? 'ring-2 ring-outline-gray-4' : ''"
          />
        </template>
      </MobileNavItem>
    </MobileNav>
  </div>
</template>

MobileNavItem

Each item takes a label, an icon (or a default slot for custom content like an avatar), and a to target. It renders a router link when navigating somewhere new, and — when it's already the current route — a button that scrolls the shell's scroll container to the top instead of re-navigating.

active controls the highlight and is independent of the current route, so one tab can stay lit across a whole section (e.g. Home across every community route) while tapping it still navigates home. When active is omitted it defaults to whether to matches the current route. The default slot receives { active } so custom content — an avatar, a badge — can react to the highlight:

vue
<MobileNavItem label="You" :to="{ name: 'More' }" :active="isMoreRoute">
  <template #default="{ active }">
    <UserAvatar :user="me" :class="{ 'ring-2 ring-outline-gray-4': active }" />
  </template>
</MobileNavItem>

Like SidebarItem, MobileNavItem is router-optional: mounted without vue-router it degrades to a plain <a>/<button> with no warnings, so it works in docs, tests, and embedded use.

API Reference

MobileNav

default
{}

MobileNavItem

Show types
typescript
import type { Component } from 'vue'
import type { RouteLocationRaw } from 'vue-router'

/**
 * `MobileNav` (the bar) has no props — it's a grid frame for `MobileNavItem`s.
 */
// eslint-disable-next-line @typescript-eslint/no-empty-object-type
export interface MobileNavProps {}

export interface MobileNavItemProps {
  /** Label under the icon; also the accessible name. */
  label: string

  /**
   * Icon CSS class, e.g. `lucide-home`, or a component. Ignored when the
   * default slot is used (for a custom glyph or an avatar).
   */
  icon?: string | Component

  /**
   * Navigation target. Renders a router link. Tapping the item while it is
   * already the current route scrolls the shell to the top instead of
   * re-navigating.
   */
  to?: RouteLocationRaw

  /**
   * Highlight this item. Independent of the current route so one tab can stay
   * lit across a whole section (e.g. Home across community routes). Defaults to
   * whether `to` resolves to the current route.
   */
  active?: boolean
}
label*
string

Label under the icon; also the accessible name.

icon
string | Component

Icon CSS class, e.g. `lucide-home`, or a component. Ignored when the default slot is used (for a custom glyph or an avatar).

to
string | kt | Tt

Navigation target. Renders a router link. Tapping the item while it is already the current route scrolls the shell to the top instead of re-navigating.

active
boolean

Highlight this item. Independent of the current route so one tab can stay lit across a whole section (e.g. Home across community routes). Defaults to whether `to` resolves to the current route.

default
{ active: boolean; }
click
[event: MouseEvent]