Navlist

A vertical navigation list for sidebars, with optional groups and badges.

Basic Usage

<c-ui.navlist class="w-64">
    <c-ui.navlist.item href="/dashboard" current>Dashboard</c-ui.navlist.item>
    <c-ui.navlist.item href="/projects">Projects</c-ui.navlist.item>
    <c-ui.navlist.item href="/team">Team</c-ui.navlist.item>
    <c-ui.navlist.item href="/settings" badge="3">Settings</c-ui.navlist.item>
</c-ui.navlist>
{% cotton ui.navlist class="w-64" %}
    {% cotton ui.navlist.item href="/dashboard" current %}Dashboard{% endcotton %}
    {% cotton ui.navlist.item href="/projects" %}Projects{% endcotton %}
    {% cotton ui.navlist.item href="/team" %}Team{% endcotton %}
    {% cotton ui.navlist.item href="/settings" badge="3" %}Settings{% endcotton %}
{% endcotton %}

Variants

The default fills the active row. Set variant="sidebar" on the navlist for an accent left-bar rail, common in docs sidebars.

{# default: filled rows #}
<c-ui.navlist>
    <c-ui.navlist.item href="/dashboard" current>Dashboard</c-ui.navlist.item>
    <c-ui.navlist.item href="/projects">Projects</c-ui.navlist.item>
    <c-ui.navlist.item href="/settings">Settings</c-ui.navlist.item>
</c-ui.navlist>

{# sidebar: accent left-bar rail #}
<c-ui.navlist variant="sidebar">
    <c-ui.navlist.item href="/dashboard" current>Dashboard</c-ui.navlist.item>
    <c-ui.navlist.item href="/projects">Projects</c-ui.navlist.item>
    <c-ui.navlist.item href="/settings">Settings</c-ui.navlist.item>
</c-ui.navlist>
{# default: filled rows #}
{% cotton ui.navlist %}
    {% cotton ui.navlist.item href="/dashboard" current %}Dashboard{% endcotton %}
    {% cotton ui.navlist.item href="/projects" %}Projects{% endcotton %}
    {% cotton ui.navlist.item href="/settings" %}Settings{% endcotton %}
{% endcotton %}

{# sidebar: accent left-bar rail #}
{% cotton ui.navlist variant="sidebar" %}
    {% cotton ui.navlist.item href="/dashboard" current %}Dashboard{% endcotton %}
    {% cotton ui.navlist.item href="/projects" %}Projects{% endcotton %}
    {% cotton ui.navlist.item href="/settings" %}Settings{% endcotton %}
{% endcotton %}

Groups

Group related items under a heading with navlist.group.

<c-ui.navlist class="w-64">
    <c-ui.navlist.item href="/dashboard">Dashboard</c-ui.navlist.item>
    <c-ui.navlist.item href="/projects">Projects</c-ui.navlist.item>

    <c-ui.navlist.group heading="Account">
        <c-ui.navlist.item href="/profile" current>Profile</c-ui.navlist.item>
        <c-ui.navlist.item href="/settings">Settings</c-ui.navlist.item>
        <c-ui.navlist.item href="/billing">Billing</c-ui.navlist.item>
    </c-ui.navlist.group>
</c-ui.navlist>
{% cotton ui.navlist class="w-64" %}
    {% cotton ui.navlist.item href="/dashboard" %}Dashboard{% endcotton %}
    {% cotton ui.navlist.item href="/projects" %}Projects{% endcotton %}

    {% cotton ui.navlist.group heading="Account" %}
        {% cotton ui.navlist.item href="/profile" current %}Profile{% endcotton %}
        {% cotton ui.navlist.item href="/settings" %}Settings{% endcotton %}
        {% cotton ui.navlist.item href="/billing" %}Billing{% endcotton %}
    {% endcotton %}
{% endcotton %}

Expandable Groups

Make a group collapsible with :expandable, and set its initial state with :expanded.

<c-ui.navlist class="w-64">
    <c-ui.navlist.item href="/dashboard">Dashboard</c-ui.navlist.item>

    <c-ui.navlist.group heading="Account" expandable expanded>
        <c-ui.navlist.item href="/profile">Profile</c-ui.navlist.item>
        <c-ui.navlist.item href="/settings">Settings</c-ui.navlist.item>
    </c-ui.navlist.group>

    <c-ui.navlist.group heading="Team" expandable :expanded="False">
        <c-ui.navlist.item href="/team/members">Members</c-ui.navlist.item>
        <c-ui.navlist.item href="/team/invitations">Invitations</c-ui.navlist.item>
    </c-ui.navlist.group>
</c-ui.navlist>
{% cotton ui.navlist class="w-64" %}
    {% cotton ui.navlist.item href="/dashboard" %}Dashboard{% endcotton %}

    {% cotton ui.navlist.group heading="Account" expandable expanded %}
        {% cotton ui.navlist.item href="/profile" %}Profile{% endcotton %}
        {% cotton ui.navlist.item href="/settings" %}Settings{% endcotton %}
    {% endcotton %}

    {% cotton ui.navlist.group heading="Team" expandable :expanded="False" %}
        {% cotton ui.navlist.item href="/team/members" %}Members{% endcotton %}
        {% cotton ui.navlist.item href="/team/invitations" %}Invitations{% endcotton %}
    {% endcotton %}
{% endcotton %}

With Badges

Show counts or labels with the badge prop on items.

<c-ui.navlist class="w-64">
    <c-ui.navlist.item href="/home">Home</c-ui.navlist.item>
    <c-ui.navlist.item href="/inbox" badge="12">Inbox</c-ui.navlist.item>
    <c-ui.navlist.item href="/contacts">Contacts</c-ui.navlist.item>
    <c-ui.navlist.item href="/calendar" badge="Pro" badge_color="lime" badge_variant="solid">Calendar</c-ui.navlist.item>
</c-ui.navlist>
{% cotton ui.navlist class="w-64" %}
    {% cotton ui.navlist.item href="/home" %}Home{% endcotton %}
    {% cotton ui.navlist.item href="/inbox" badge="12" %}Inbox{% endcotton %}
    {% cotton ui.navlist.item href="/contacts" %}Contacts{% endcotton %}
    {% cotton ui.navlist.item href="/calendar" badge="Pro" badge_color="lime" badge_variant="solid" %}Calendar{% endcotton %}
{% endcotton %}

Persist scroll

For a long sidebar that scrolls on its own, add persist_scroll so the list keeps its scroll position across full page loads. It only restores when you navigated via a link inside the navlist, so arriving from the top nav or an external link still starts at the top instead of resuming a stale position. The navlist has to be its own scroll container, so give it a max-height and overflow-y-auto.

The sidebar on this very site uses it: scroll the sidebar down, open a component, and it stays where you left it.

<c-ui.navlist variant="sidebar" persist_scroll
              class="max-h-[calc(100dvh-5rem)] overflow-y-auto">
    <c-ui.navlist.item href="/dashboard" current>Dashboard</c-ui.navlist.item>
    <c-ui.navlist.item href="/projects">Projects</c-ui.navlist.item>
    <c-ui.navlist.item href="/settings">Settings</c-ui.navlist.item>
</c-ui.navlist>
{% cotton ui.navlist
              variant="sidebar"
              persist_scroll
              class="max-h-[calc(100dvh-5rem)] overflow-y-auto"
          %}
    {% cotton ui.navlist.item href="/dashboard" current %}Dashboard{% endcotton %}
    {% cotton ui.navlist.item href="/projects" %}Projects{% endcotton %}
    {% cotton ui.navlist.item href="/settings" %}Settings{% endcotton %}
{% endcotton %}

You only need scroll_key when more than one persisted navlist appears on the same page, so each keeps its own saved position. A single sidebar can leave it at the default.

API Reference

Navlist Props

Name Description Type Options Default
variant Empty fills the active row; sidebar uses an accent left-bar rail. str
sidebar
:persist_scroll Remember the navlist's scroll position across page loads (sessionStorage), restoring it only when you navigated via a link inside this navlist. For long sidebars this stops the list jumping to the top on every in-nav navigation, without resuming a stale position when you arrive from an unrelated page. The navlist must be its own scroll container (give it a max-height plus overflow-y-auto). bool
TrueFalse
False
scroll_key Optional storage key for persist_scroll. Only needed when more than one persisted navlist renders on the same page; give each a unique key. A single sidebar can leave the default. str navlist
slot (default) The navigation items and groups.

Navlist.Item Props

Name Description Type Options Default
href URL the item links to. str #
:current Applies active styling to mark the current page. bool
TrueFalse
False
badge Trailing badge content. str
badge_color Badge color. str
zincredorangeamberyellowlimegreenemeraldtealcyanskyblueindigovioletpurplefuchsiapinkrose
zinc
badge_variant Badge variant. str
pillsolid

Navlist.Group Props

Name Description Type Options Default
heading Group heading text. str
:expandable Makes the group collapsible with a toggle. bool
TrueFalse
False
:expanded Initial open state when expandable. bool
TrueFalse
True
slot (default) The group's items.