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

```html
<button type="button" class="btn btn-primary me-2 mb-2">Primary</button>
<button type="button" class="btn btn-secondary me-2 mb-2">Secondary</button>
<button type="button" class="btn btn-success me-2 mb-2">Success</button>
<button type="button" class="btn btn-danger me-2 mb-2">Danger</button>
<button type="button" class="btn btn-warning me-2 mb-2">Warning</button>
<button type="button" class="btn btn-info me-2 mb-2">Info</button>
<button type="button" class="btn btn-light me-2 mb-2">Light</button>
<button type="button" class="btn btn-dark me-2 mb-2">Dark</button>
<button type="button" class="btn btn-link me-2 mb-2">Link</button>
```
```tsx code
<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:

```tsx
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

| Class | Effect |
| --- | --- |
| `btn` | The base class, needed on every button |
| `btn-primary`, `btn-secondary`, `btn-success`, `btn-danger`, `btn-warning`, `btn-info`, `btn-light`, `btn-dark` | A filled button in a theme color |
| `btn-outline-<color>` | The border and text in the color, filled on hover |
| `btn-link` | A button that looks like a link |
| `btn-sm`, `btn-lg` | A 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` |
| `active` | The 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.

```html
<button type="button" class="btn btn-outline-primary me-2 mb-2">Primary</button>
<button type="button" class="btn btn-outline-secondary me-2 mb-2">Secondary</button>
<button type="button" class="btn btn-outline-success me-2 mb-2">Success</button>
<br>
<button type="button" class="btn btn-primary btn-sm me-2">Small</button>
<button type="button" class="btn btn-primary me-2">Medium</button>
<button type="button" class="btn btn-primary btn-lg">Large</button>
```
```tsx code
<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:

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

```html
<button type="button" class="btn btn-primary me-2">Save changes</button>
<button type="button" class="btn btn-primary" disabled>
  <span class="spinner-border spinner-border-sm me-2" aria-hidden="true"></span>
  Saving...
</button>
```
```tsx code
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](/react/docs/components/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:

```html
<button type="button" class="btn btn-outline-primary me-2" data-bs-toggle="button" aria-pressed="false">Pin to dashboard</button>
<button type="button" class="btn btn-outline-primary active" data-bs-toggle="button" aria-pressed="true">Pinned</button>
```
```tsx code
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>
  );
}
```

## Icons and links

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:

```tsx
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:

```tsx
import { Link } from "react-router";

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

## Rounded and square buttons

> 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.

```html
<button type="button" class="btn btn-pill btn-primary me-2">Rounded</button>
<button type="button" class="btn btn-square btn-primary">Square</button>
```
```tsx code
<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}`.

## Related

- [Next.js Bootstrap buttons](/nextjs/docs/components/buttons/): the same markup in AdminKit for Next.js
- [Bootstrap 5 buttons](/bootstrap/docs/components/buttons/): the buttons of the HTML version
- [Button group](/react/docs/components/button-group/): buttons joined in a group or a toolbar
- [Buttons demo](https://react-demo.adminkit.io/ui-buttons)

---

Canonical HTML page: https://adminkit.io/react/docs/components/buttons/

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
