Navlist

Vertical navigation with sections, icons, badges, and active state.

Basic

.heex
<.navlist>
  <.navlink navigate={~p"/dashboard"}>Dashboard</.navlink>
  <.navlink navigate={~p"/projects"}>Projects</.navlink>
  <.navlink navigate={~p"/team"}>Team</.navlink>
  <.navlink navigate={~p"/settings"}>Settings</.navlink>
</.navlist>

With icons

.heex
<.navlist>
  <.navlink navigate={~p"/dashboard"} active>
    <.icon name="hero-home" class="icon" /> Dashboard
  </.navlink>
  <.navlink navigate={~p"/projects"}>
    <.icon name="hero-folder" class="icon" /> Projects
  </.navlink>
  <.navlink navigate={~p"/team"}>
    <.icon name="hero-users" class="icon" /> Team
  </.navlink>
  <.navlink navigate={~p"/settings"}>
    <.icon name="hero-cog-6-tooth" class="icon" /> Settings
  </.navlink>
</.navlist>

Section heading

Set heading to render a navheading as the first child without extra markup.

.heex
<.navlist heading="Workspace">
  <.navlink navigate={~p"/dashboard"} active>
    <.icon name="hero-home" class="icon" /> Dashboard
  </.navlink>
  <.navlink navigate={~p"/projects"}>
    <.icon name="hero-folder" class="icon" /> Projects
  </.navlink>
  <.navlink navigate={~p"/analytics"}>
    <.icon name="hero-chart-bar" class="icon" /> Analytics
  </.navlink>
</.navlist>

Multiple sections

Stacked navlists separate automatically through a sibling selector, with no wrapper or spacing utility needed.

.heex
<.navlist heading="Workspace">
  <.navlink navigate={~p"/dashboard"} active>
    <.icon name="hero-home" class="icon" /> Dashboard
  </.navlink>
  <.navlink navigate={~p"/projects"}>
    <.icon name="hero-folder" class="icon" /> Projects
  </.navlink>
  <.navlink navigate={~p"/analytics"}>
    <.icon name="hero-chart-bar" class="icon" /> Analytics
  </.navlink>
</.navlist>

<.navlist heading="Account">
  <.navlink navigate={~p"/profile"}>
    <.icon name="hero-user" class="icon" /> Profile
  </.navlink>
  <.navlink navigate={~p"/billing"}>
    <.icon name="hero-credit-card" class="icon" /> Billing
  </.navlink>
  <.navlink navigate={~p"/preferences"}>
    <.icon name="hero-cog-6-tooth" class="icon" /> Preferences
  </.navlink>
</.navlist>

Custom heading

Render navheading directly to add uppercase captions, badges, or any markup richer than plain text.

.heex
<.navlist>
  <.navheading class="text-xs uppercase tracking-wider text-foreground-softest">
    Customers
  </.navheading>
  <.navlink navigate={~p"/customers/active"}>Active</.navlink>
  <.navlink navigate={~p"/customers/leads"}>Leads</.navlink>
  <.navlink navigate={~p"/customers/inactive"}>Inactive</.navlink>
</.navlist>

Trailing badges

Push counters and status pills to the right edge with ml-auto.

.heex
<.navlist heading="Inbox">
  <.navlink navigate={~p"/inbox/unread"} active>
    <.icon name="hero-inbox" class="icon" /> Unread
    <.badge color="danger" class="ml-auto rounded-full">23</.badge>
  </.navlink>
  <.navlink navigate={~p"/inbox/starred"}>
    <.icon name="hero-star" class="icon" /> Starred
    <.badge class="ml-auto rounded-full">5</.badge>
  </.navlink>
  <.navlink navigate={~p"/inbox/drafts"}>
    <.icon name="hero-document" class="icon" /> Drafts
    <.badge color="info" class="ml-auto rounded-full">3</.badge>
  </.navlink>
  <.navlink navigate={~p"/inbox/archive"}>
    <.icon name="hero-archive-box" class="icon" /> Archive
  </.navlink>
</.navlist>

Icon-only with tooltips

Wrap each navlink in a tooltip so collapsed sidebars still surface labels on hover.

.heex
<.navlist class="w-min space-y-2">
  <.tooltip value="Dashboard" placement="right">
    <.navlink navigate={~p"/dashboard"} active>
      <.icon name="hero-home" class="icon" />
    </.navlink>
  </.tooltip>
  <.tooltip value="Projects" placement="right">
    <.navlink navigate={~p"/projects"}>
      <.icon name="hero-folder" class="icon" />
    </.navlink>
  </.tooltip>
  <.tooltip value="Team" placement="right">
    <.navlink navigate={~p"/team"}>
      <.icon name="hero-users" class="icon" />
    </.navlink>
  </.tooltip>
  <.tooltip value="Settings" placement="right">
    <.navlink navigate={~p"/settings"}>
      <.icon name="hero-cog-6-tooth" class="icon" />
    </.navlink>
  </.tooltip>
</.navlist>

Pick the navigation attribute by intent. navigate handles live mounts, patch updates URLs in place, href opens external links, phx-click runs server actions without navigating.

.heex
<.navlist heading="Account">
  <.navlink navigate={~p"/dashboard"}>
    <.icon name="hero-home" class="icon" /> Dashboard
  </.navlink>
  <.navlink patch={~p"/messages?folder=inbox"} replace>
    <.icon name="hero-envelope" class="icon" /> Inbox
  </.navlink>
  <.navlink href="https://docs.example.com" target="_blank" rel="noopener">
    <.icon name="hero-book-open" class="icon" /> Documentation
  </.navlink>
  <.navlink phx-click="sign_out">
    <.icon name="hero-arrow-right-on-rectangle" class="icon" /> Sign out
  </.navlink>
</.navlist>

Expandable group

Toggle a data-expanded attribute on the trigger and animate the panel from grid-rows-[0fr] to grid-rows-[1fr], no server round-trip required.

.heex
<.navlist>
  <.navlink phx-click={JS.toggle_attribute({"data-expanded", ""})}>
    <.icon name="hero-users" class="icon" /> Customers
    <.icon
      name="hero-chevron-right"
      class="size-3 ml-auto text-foreground-softest in-data-expanded:rotate-90 transition-transform duration-200"
    />
  </.navlink>
  <div class="grid grid-rows-[0fr] [[data-expanded]~&]:grid-rows-[1fr] transition-all duration-200">
    <div class="overflow-hidden px-4 -mr-4 border-l border-base ml-3">
      <.navlist>
        <.navlink navigate={~p"/customers/active"}>Active</.navlink>
        <.navlink navigate={~p"/customers/leads"}>Leads</.navlink>
        <.navlink navigate={~p"/customers/inactive"}>Inactive</.navlink>
      </.navlist>
    </div>
  </div>

  <.navlink navigate={~p"/subscriptions"}>
    <.icon name="hero-arrow-path" class="icon" /> Subscriptions
  </.navlink>
</.navlist>

Nested expandable

.heex
<.navlist>
  <.navlink phx-click={JS.toggle_attribute({"data-expanded", ""})}>
    <.icon name="hero-building-office" class="icon" /> Organization
    <.icon
      name="hero-chevron-right"
      class="size-3 ml-auto text-foreground-softest in-data-expanded:rotate-90 transition-transform duration-200"
    />
  </.navlink>
  <div class="grid grid-rows-[0fr] [[data-expanded]~&]:grid-rows-[1fr] transition-all duration-200">
    <div class="overflow-hidden px-4 -mr-4 border-l border-base ml-3">
      <.navlist>
        <.navlink navigate={~p"/org/departments"}>Departments</.navlink>

        <.navlink phx-click={JS.toggle_attribute({"data-expanded", ""})}>
          Teams
          <.icon
            name="hero-chevron-right"
            class="size-3 ml-auto text-foreground-softest in-data-expanded:rotate-90 transition-transform duration-200"
          />
        </.navlink>
        <div class="grid grid-rows-[0fr] [[data-expanded]+&]:grid-rows-[1fr] transition-all duration-200">
          <div class="overflow-hidden px-4 border-l border-base ml-3">
            <.navlist>
              <.navlink navigate={~p"/teams/development"}>Development</.navlink>
              <.navlink navigate={~p"/teams/design"}>Design</.navlink>
              <.navlink navigate={~p"/teams/marketing"}>Marketing</.navlink>
            </.navlist>
          </div>
        </div>

        <.navlink navigate={~p"/org/roles"}>Roles</.navlink>
      </.navlist>
    </div>
  </div>
</.navlist>

Customization

Custom class values compose with the base styles, and data-active: Tailwind variants extend the active state without server logic.

.heex
<.navlist>
  <.navheading class="text-xs uppercase font-medium text-zinc-400">
    Customers
  </.navheading>

  <.navlink
    navigate={~p"/customers"}
    class={[
      "group py-2 relative",
      "hover:text-blue-600 hover:bg-white hover:shadow-sm hover:ring-1 ring-zinc-200",
      "hover:after:absolute hover:after:inset-y-0 hover:after:left-0",
      "hover:after:my-1.5 hover:after:w-1 hover:after:bg-blue-600 hover:after:rounded-r-md"
    ]}
  >
    <.icon class="icon text-zinc-500 group-hover:text-blue-600" name="hero-users" />
    <span class="grow">Customers</span>
  </.navlink>

  <.navlink
    navigate={~p"/subscriptions"}
    active
    class={[
      "group py-2 relative",
      "data-active:text-blue-600 data-active:bg-white data-active:shadow-sm data-active:ring-1 ring-zinc-200",
      "data-active:after:absolute data-active:after:inset-y-0 data-active:after:left-0",
      "data-active:after:my-1.5 data-active:after:w-1 data-active:after:bg-blue-600 data-active:after:rounded-r-md"
    ]}
  >
    <.icon class="icon text-zinc-500 group-data-active:text-blue-600" name="hero-shopping-bag" />
    <span class="grow">Subscriptions</span>
    <.badge class="ml-auto bg-blue-100 text-blue-700">23</.badge>
  </.navlink>

  <.navlink
    navigate={~p"/products"}
    class="group py-2 hover:text-blue-600 hover:bg-white hover:shadow-sm hover:ring-1 ring-zinc-200"
  >
    <.icon class="icon text-zinc-500 group-hover:text-blue-600" name="hero-cube" />
    <span class="grow">Products</span>
  </.navlink>
</.navlist>