AdminKitDocs
Components

Next.js Bootstrap Badge

Next.js Bootstrap badge markup that renders in server components: theme and light colors, pills, status badges from data and counters on buttons.

A badge puts a short label or a number where the user is already looking: a status in a table, or a count on a button. In AdminKit for Next.js a badge is Bootstrap's .badge class on a <span>, written in JSX. Badges are in the free and PRO versions.

Badges have no state, so they fit any App Router page. Pages are server components that render on the server, and a badge renders there with no "use client". A status badge computed from your data ends up in the static HTML of the page.

Example

<span className="badge bg-primary me-1">Primary</span>
<span className="badge bg-secondary me-1">Secondary</span>
<span className="badge bg-success me-1">Success</span>
<span className="badge bg-danger me-1">Danger</span>
<span className="badge bg-warning me-1">Warning</span>
<span className="badge bg-info">Info</span>

A badge is set at 80% of its parent's font size, so it scales with the text around it.

Classes

ClassEffect
badgeThe base class
bg-<color>Filled in a theme color: bg-primary, bg-success, ...
badge-<color>-lightAdminKit: a 15% tint of the color with darker text, such as badge-success-light
rounded-pillRound ends
text-bg-<color>Bootstrap's background with a matching text color

Light and pill badges

Pick a light badge when a filled one would draw too much attention, for example in a long column of statuses. AdminKit adds the light variants in src/scss/3-components/_badges.scss. The text of a light badge keeps a 4.5:1 contrast on its tint in both color modes. AdminKit PRO uses them on the account pages, for statuses such as "Invited".

<span className="badge badge-primary-light me-1">Draft</span>
<span className="badge badge-success-light me-1">Active</span>
<span className="badge badge-warning-light me-1">Invited</span>
<span className="badge bg-primary rounded-pill me-1">14</span>
<span className="badge bg-danger rounded-pill">99+</span>

Status badge from data

Statuses usually come from your data, so let a small component map each status to a color. It has no hooks and no handlers, so it needs no "use client". A page renders it on the server, and a client component can use it too:

src/components/status-badge.tsx
type Status = "paid" | "pending" | "failed";

const statusColors: Record<Status, string> = {
  paid: "success",
  pending: "warning",
  failed: "danger",
};

export function StatusBadge({ status }: { status: Status }) {
  return (
    <span className={`badge badge-${statusColors[status]}-light text-capitalize`}>
      {status}
    </span>
  );
}

Counters

A counter shows how many things are waiting, such as unread messages. That count can change while the page is open, so it comes from state in a client component. The badge renders only when the count is above zero. A visually hidden text says what the number means:

export function InboxButton({ unread }: { unread: number }) {
  return (
    <button type="button" className="btn btn-primary position-relative">
      Inbox
      {unread > 0 && (
        <span className="position-absolute top-0 start-100 translate-middle badge rounded-pill bg-danger">
          {unread > 99 ? "99+" : unread}
          <span className="visually-hidden">unread messages</span>
        </span>
      )}
    </button>
  );
}

InboxButton itself has no state. The client component that fetches the count passes unread to it. With a static export, that fetch runs in the browser.

Accessibility

  • Screen readers read the badge text only. Give a bare number context with a visually-hidden text or an aria-label on the button.
  • Keep a word in a status badge, such as "Failed". The color alone does not tell every user the status.
  • A counter that changes is not announced. Use a toast or an aria-live region for updates the user must hear.

Last updated on

On this page