AdminKitDocs
Components

Next.js Bootstrap List Group

Next.js Bootstrap list group markup: static lists in server components, next/link items with the active route from usePathname, badges and cards.

List groups line up related items, such as messages, folders or the links of a settings page. The demo uses them in cards, in the navbar's message and notification menus, and in the settings navigation. You write them as Bootstrap's .list-group and .list-group-item markup in JSX, usually mapped from an array. There is no component to import. List groups work in the free and PRO versions of AdminKit for Next.js.

In the App Router, pages are server components: they render on the server and have no state. A list without state renders there with no "use client". A list that tracks a selected item, or highlights the current route with usePathname, needs state or a hook that runs in the browser. It goes in a client component, a file that starts with "use client".

Example

const tasks = [
  { id: 1, title: "Design review" },
  { id: 2, title: "Sprint planning" },
  { id: 3, title: "Release notes" },
];

<ul className="list-group">
  {tasks.map((task) => (
    <li key={task.id} className="list-group-item">
      {task.title}
    </li>
  ))}
</ul>

The key is a stable value from the data, such as an id, so React matches items correctly when the list changes.

Classes

ClassEffect
list-groupThe container: <ul>, <ol> or <div>
list-group-itemAn item
list-group-item-actionHover and focus styles for link and button items
active, disabledThe selected or disabled item
list-group-flushDrops the outer borders and corners, for a list in a card
list-group-numberedCSS numbers on an <ol>
list-group-horizontalThe items in a row; list-group-horizontal-md from a breakpoint
list-group-item-<color>An item in a theme color, such as list-group-item-success

Selectable items

When the user picks an item, such as a folder, make each item a <button> with list-group-item-action. The selection is state, so the component is a client component:

src/components/folder-list.tsx
"use client";

import { useState } from "react";
import { cx } from "@/lib/cx";

const folders = ["Inbox", "Sent", "Archive"];

export function FolderList() {
  const [folder, setFolder] = useState("Inbox");

  return (
    <div className="list-group">
      {folders.map((name) => (
        <button
          key={name}
          type="button"
          className={cx("list-group-item list-group-item-action", name === folder && "active")}
          aria-current={name === folder ? "true" : undefined}
          onClick={() => setFolder(name)}
        >
          {name}
        </button>
      ))}
    </div>
  );
}

For a list of links to other pages, render each item with next/link. A list of links without an active item stays a server component. To highlight the current page, read the route with usePathname. That hook runs in the browser, so the component needs "use client", as the sidebar does:

src/components/settings-nav.tsx
"use client";

import Link from "next/link";
import { usePathname } from "next/navigation";
import { cx } from "@/lib/cx";

const sections = [
  { href: "/pages-settings", label: "Account" },
  { href: "/pages-profile", label: "Profile" },
];

export function SettingsNav() {
  const pathname = usePathname();

  return (
    <div className="list-group list-group-flush">
      {sections.map((section) => (
        <Link
          key={section.href}
          href={section.href}
          className={cx("list-group-item list-group-item-action", pathname === section.href && "active")}
          aria-current={pathname === section.href ? "page" : undefined}
        >
          {section.label}
        </Link>
      ))}
    </div>
  );
}

a.list-group-item has no underline in AdminKit.

With badges

Put a count at the end of an item with a badge and the flex utilities.

<ul className="list-group">
  {queues.map((queue) => (
    <li
      key={queue.id}
      className="list-group-item d-flex justify-content-between align-items-center"
    >
      {queue.name}
      <span className={`badge bg-${queue.color} rounded-pill`}>{queue.count}</span>
    </li>
  ))}
</ul>

In a card

A list inside a card needs no border of its own. With list-group-flush, the list runs edge to edge in a .card, as on the cards demo:

<div className="card">
  <div className="card-header">
    <h2 className="card-title mb-0">Recent activity</h2>
  </div>
  <ul className="list-group list-group-flush">
    <li className="list-group-item">Invoice #1042 paid</li>
    <li className="list-group-item">New client added</li>
    <li className="list-group-item">Project archived</li>
  </ul>
</div>

Accessibility

  • Mark the selected or current item with aria-current as well as active.
  • Use <button> items for actions, links for navigation, and no .btn classes on list group items.
  • A checkbox in an item needs a <label htmlFor> or an aria-label.
  • A disabled <button> item takes the disabled attribute. A disabled link needs aria-disabled="true" and tabIndex={-1}.

Last updated on

On this page