Lumeo
Package LumeoA11ynavigationDepends onbuttonTested Tier 3 · 52 testsView source ↗

Bottom Nav

Mobile-first bottom tab bar with icons, labels, badges, safe-area padding, and an optional floating action button. Pair with Sidebar for responsive app layouts.

Installation

dotnet add package Lumeo

One-time app setup (AddLumeo(), CSS & JS) is covered in the installation guide.

Usage

@using Lumeo

<BottomNav />
Tested Tier 3 · A11y + behavior
all components →
Render Behavior A11y Keyboard Scale E2E

52 tests across 8 files. Auto-generated from the test suite.

When to Use

  • Mobile or touch-first apps that need 3–5 top-level destinations always reachable
  • As the small-screen half of a responsive pair: Sidebar on desktop, BottomNav on mobile
  • Thumb-zone primary actions (center FAB for "Create", "Compose", "Scan")

Not for: deep navigation hierarchies, admin panels, or more than ~5 items. Use Sidebar, Tabs, or Navigation Menu for those.

App content area
Unread indicators
Primary action in the thumb zone
Click a tab — active highlight glides in.

Pick a value to see it applied live. Default anchors full-width to the bottom; Pill floats as a centered rounded bar.

App content area
Elevated centered pill variant
Tap a tab — it scales for a tactile feel.

Responsive: Sidebar ↔ BottomNav

The standard mobile pattern: show the full <Sidebar> on md and up, and a 4-item <BottomNav> below it. Pick the 4 most-used items — do not mirror the full sidebar tree; bury the rest behind a "More" item or a Sheet.

<!-- MainLayout.razor --> <div class="min-h-screen flex"> <!-- Sidebar: visible on md and up --> <SidebarProvider Class="hidden md:flex"> <SidebarComponent> <SidebarHeader>Acme Inc</SidebarHeader> <SidebarContent> <SidebarMenu> <SidebarMenuItem> <SidebarMenuButton Href="/" IsActive="@true"> <IconContent><Icon Name="House" Size="Lumeo.Size.Sm" /></IconContent> <LabelContent>Home</LabelContent> </SidebarMenuButton> </SidebarMenuItem> <!-- …full sidebar tree… --> </SidebarMenu> </SidebarContent> </SidebarComponent> </SidebarProvider> <!-- Main content: reserve space at the bottom on mobile --> <main class="flex-1 pb-16 md:pb-0"> @Body </main> </div> <!-- BottomNav: visible below md. Pick the 4 most-used items only. --> <BottomNav Class="md:hidden"> <BottomNavItem Href="/" Label="Home"> <IconContent><Icon Name="House" Size="Lumeo.Size.Sm" /></IconContent> </BottomNavItem> <BottomNavItem Href="/search" Label="Search"> <IconContent><Icon Name="Search" Size="Lumeo.Size.Sm" /></IconContent> </BottomNavItem> <BottomNavItem Href="/inbox" Label="Inbox"> <IconContent><Icon Name="Inbox" Size="Lumeo.Size.Sm" /></IconContent> <Badge>3</Badge> </BottomNavItem> <BottomNavItem Href="/profile" Label="Profile"> <IconContent><Icon Name="User" Size="Lumeo.Size.Sm" /></IconContent> </BottomNavItem> </BottomNav>

Two simple rules: the Sidebar is hidden md:flex, the BottomNav is md:hidden. Add pb-16 md:pb-0 to your scroll container so content isn't hidden behind the fixed bar on mobile.

API Reference

BottomNav

Prop Type Default Description
ChildContent RenderFragment? The BottomNavItem (and optionally BottomNavFab) elements rendered inside the nav bar.
AriaLabel string? Accessible label for the nav landmark. Defaults to "Bottom navigation".
Fixed bool true When true (default), pins to the bottom of the viewport with position: fixed. Set false to render inline.
ReserveSpace bool false When true (and Fixed is on), also renders an in-flow, aria-hidden spacer equal to the nav's height plus the bottom safe-area inset, so the fixed bar never overlaps the end of the page content — no consumer padding-bottom hack needed. The height is driven by the --lumeo-bottom-nav-height CSS variable (default 3.5rem); override it if your nav is taller (e.g. the Pill variant or two-line labels). Place BottomNav at the end of the scrolling content so the spacer reserves room there. Default false.
Variant BottomNavVariant BottomNavVariant.Default Visual variant. Default is a solid bar with a top border; Pill is a floating centered pill.
AnimatedIndicator bool false When true, each BottomNavItem gets smooth background-color and color transitions, and the small active pill transitions its transform with a spring easing. Default is false.
Class string? Additional CSS classes merged onto the root nav element.
AdditionalAttributes Dictionary<string, object>? Unmatched attributes are splatted onto the root nav element.

BottomNavItem

Prop Type Default Description
Href string? Destination URL. When set, item renders as an <a>; otherwise as a <button>.
Label string? Text label shown under the icon. Also used as the accessible name.
IconContent RenderFragment? Icon slot (use Icon or <SvgGlyph>). The Content suffix avoids colliding with the <Icon> component.
Badge RenderFragment? Optional badge (count, dot, or any content) shown on the icon. Int, string, or RenderFragment.
IconSize string "h-5 w-5" Size class applied to the icon container. Defaults to h-5 w-5 to match the original visual. Override to fit larger content like avatars (e.g. "h-7 w-7").
IsActive bool? Manual active-state override. When null, active is inferred from the current route matching Href.
PressEffect Button.ButtonPressEffect Button.ButtonPressEffect.None Visual feedback when the item is pressed. Default is None. Scale is recommended for a tactile feel on mobile.
Class string? Additional CSS classes merged onto the root button/anchor element.
AdditionalAttributes Dictionary<string, object>? Unmatched attributes are splatted onto the root button/anchor element.

Events

OnClick EventCallback Click handler — fires for both <a> and <button> renderings (link navigation still proceeds).

BottomNavFab

Prop Type Default Description
Href string? Optional destination URL. When set, renders as an anchor; otherwise a button.
AriaLabel* string? Accessible label. Required because FABs are usually icon-only. Defaults to "Primary action".
ChildContent RenderFragment? The icon (or other content) rendered inside the floating action button.
Class string? Additional CSS classes merged onto the root button/anchor element.
AdditionalAttributes Dictionary<string, object>? Unmatched attributes are splatted onto the root button/anchor element.

Events

OnClick EventCallback Invoked when the FAB is activated. Ignored when Href is set (the anchor navigates instead).
  • Sidebar — Use on desktop in the responsive pair
  • Tabs — For switching views within a page (not top-level nav)
  • Speed Dial — Alternative quick-action surface
  • Sheet — Bottom sheet for "More" destinations that don't fit in 4 tabs