AdminKitDocs
Components

React Bootstrap Buttons

React Bootstrap button markup in AdminKit: theme and outline colors, sizes, icon buttons, toggle buttons and a loading state driven by React state.

Buttons are how people act on a page: save a form, open a dialog, start an export. In AdminKit for React a button is Bootstrap's .btn classes on a <button> or a link.

There is no Button component to import. You write the markup in JSX with className, and set attributes such as disabled and aria-pressed from React state. Buttons are part of the free and PRO versions.

Example

<button type="button" className="btn btn-primary me-2 mb-2">Primary</button>
<button type="button" className="btn btn-secondary me-2 mb-2">Secondary</button>
<button type="button" className="btn btn-success me-2 mb-2">Success</button>
<button type="button" className="btn btn-danger me-2 mb-2">Danger</button>
<button type="button" className="btn btn-warning me-2 mb-2">Warning</button>
<button type="button" className="btn btn-info me-2 mb-2">Info</button>
<button type="button" className="btn btn-light me-2 mb-2">Light</button>
<button type="button" className="btn btn-dark me-2 mb-2">Dark</button>
<button type="button" className="btn btn-link me-2 mb-2">Link</button>

JSX drops the line breaks between inline elements, so the buttons above are spaced with me-2. The buttons demo renders its rows from a list of colors instead, and puts a {" "} after each button to keep the gap:

import { Fragment } from "react";

const colors = ["primary", "secondary", "success", "danger", "warning", "info"];

export function ColorButtons() {
  return (
    <div className="mb-3">
      {colors.map((color) => (
        <Fragment key={color}>
          <button type="button" className={`btn btn-${color}`}>
            {color}
          </button>{" "}
        </Fragment>
      ))}
    </div>
  );
}

Classes

ClassEffect
btnThe base class, needed on every button
btn-primary, btn-secondary, btn-success, btn-danger, btn-warning, btn-info, btn-light, btn-darkA filled button in a theme color
btn-outline-<color>The border and text in the color, filled on hover
btn-linkA button that looks like a link
btn-sm, btn-lgA smaller or larger button
btn-facebook, btn-twitter, btn-google, btn-github, ...Brand colors, one per entry of $social-colors in src/scss/1-variables/_app.scss
activeThe pressed look of a toggle button

The styles are Bootstrap's, extended in src/scss/3-components/_buttons.scss. AdminKit gives .btn-warning dark text, because white text does not reach a 4.5:1 contrast on it. The brand colors are darkened where white text does not reach 4.5:1 either.

Outline buttons and sizes

An outline button is quieter than a filled one, which suits a second action next to the main one. Add btn-sm or btn-lg to change the size.

<button type="button" className="btn btn-outline-primary me-2 mb-2">Primary</button>
<button type="button" className="btn btn-outline-secondary me-2 mb-2">Secondary</button>
<button type="button" className="btn btn-outline-success me-2 mb-2">Success</button>

<button type="button" className="btn btn-primary btn-sm me-2">Small</button>
<button type="button" className="btn btn-primary me-2">Medium</button>
<button type="button" className="btn btn-primary btn-lg">Large</button>

Loading state

When a click starts a request, the user should see that something is happening and should not click twice. A loading button disables itself and changes its label until the request is done. In JSX, disabled is a boolean prop, so pass it the state:

Pro

The spinner in this button is only available in AdminKit PRO.

import { useState } from "react";

export function SaveButton() {
  const [saving, setSaving] = useState(false);

  async function save() {
    setSaving(true);
    try {
      await fetch("/api/settings", { method: "POST" });
    } finally {
      setSaving(false);
    }
  }

  return (
    <button type="button" className="btn btn-primary" disabled={saving} onClick={save}>
      {saving && <span className="spinner-border spinner-border-sm me-2" aria-hidden="true" />}
      {saving ? "Saving..." : "Save changes"}
    </button>
  );
}

See Spinners for the other spinner styles.

Toggle buttons

Use a toggle button for something the user turns on and off, such as pinning an item. The button keeps its pressed state in React. Add active to the class and set aria-pressed from the same state. cx from @/lib/cx joins the class names that are true:

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

export function PinButton() {
  const [pinned, setPinned] = useState(false);

  return (
    <button
      type="button"
      className={cx("btn btn-outline-primary", pinned && "active")}
      aria-pressed={pinned}
      onClick={() => setPinned(!pinned)}
    >
      {pinned ? "Pinned" : "Pin to dashboard"}
    </button>
  );
}

Put an icon next to the label, or use it alone for a compact button. Lucide icons from lucide-react render as an svg with the lucide class. Inside a .btn they are 14px. A button with only an icon needs an aria-label, so screen readers can name it:

import { Download, Trash } from "lucide-react";

<button type="button" className="btn btn-primary me-2">
  <Download /> Export
</button>
<button type="button" className="btn btn-danger" aria-label="Delete">
  <Trash />
</button>

A button that takes the user to another page is really a link. Give the .btn classes to React Router's Link, not to a <button> with an onClick that navigates:

import { Link } from "react-router";

<Link to="/pages-projects" className="btn btn-primary">
  View projects
</Link>

Rounded and square buttons

Pro

These classes are only available in AdminKit PRO.

Change the corners when a button should look softer or sharper than the rest. btn-pill rounds the ends of a button. btn-square removes its border radius.

<button type="button" className="btn btn-pill btn-primary me-2">Rounded</button>
<button type="button" className="btn btn-square btn-primary">Square</button>

Accessibility

  • Give every <button> a type. Without it, a button in a form submits the form.
  • An icon-only button needs an aria-label, as in the buttons demo.
  • A toggle button sets aria-pressed from its state, so screen readers announce it as pressed or not.
  • While a button is loading, keep a visible text such as "Saving..." and hide the spinner with aria-hidden="true".
  • For a disabled link, prefer a <button disabled>: a link with .disabled still needs aria-disabled="true" and tabIndex={-1}.

Last updated on

On this page