# 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](/nextjs/).

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

```html
<ul class="list-group">
  <li class="list-group-item">Design review</li>
  <li class="list-group-item">Sprint planning</li>
  <li class="list-group-item">Release notes</li>
</ul>
```
```tsx code
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

| Class | Effect |
| --- | --- |
| `list-group` | The container: `<ul>`, `<ol>` or `<div>` |
| `list-group-item` | An item |
| `list-group-item-action` | Hover and focus styles for link and button items |
| `active`, `disabled` | The selected or disabled item |
| `list-group-flush` | Drops the outer borders and corners, for a list in a card |
| `list-group-numbered` | CSS numbers on an `<ol>` |
| `list-group-horizontal` | The 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:

```html
<div class="list-group" style="max-width: 20rem;">
  <button type="button" class="list-group-item list-group-item-action active" aria-current="true">Inbox</button>
  <button type="button" class="list-group-item list-group-item-action">Sent</button>
  <button type="button" class="list-group-item list-group-item-action">Archive</button>
</div>
```
```tsx code title="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>
  );
}
```

## Links to routes

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:

```tsx title="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.

```html
<ul class="list-group" style="max-width: 20rem;">
  <li class="list-group-item d-flex justify-content-between align-items-center">
    Open tickets
    <span class="badge bg-primary rounded-pill">14</span>
  </li>
  <li class="list-group-item d-flex justify-content-between align-items-center">
    Overdue
    <span class="badge bg-danger rounded-pill">2</span>
  </li>
</ul>
```
```tsx code
<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:

```html
<div class="card" style="max-width: 20rem;">
  <div class="card-header">
    <h2 class="card-title mb-0">Recent activity</h2>
  </div>
  <ul class="list-group list-group-flush">
    <li class="list-group-item">Invoice #1042 paid</li>
    <li class="list-group-item">New client added</li>
    <li class="list-group-item">Project archived</li>
  </ul>
</div>
```
```tsx code
<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}`.

## Related

- [React Bootstrap list group](/react/docs/components/list-group/): the same markup in AdminKit for React (Vite)
- [Bootstrap 5 list group](/bootstrap/docs/components/list-group/): the list group of the HTML version
- [Cards](/nextjs/docs/components/card/): the container most list groups sit in
- [Cards demo](https://nextjs-demo.adminkit.io/ui-cards): a flush list group inside a card

---

Canonical HTML page: https://adminkit.io/nextjs/docs/components/list-group/

Site overview for agents: https://adminkit.io/llms.txt (expanded: https://adminkit.io/llms-full.txt) · Documentation: https://adminkit.io/bootstrap/docs/ · Questions: support@adminkit.io
