# React Bootstrap Tabs

React Bootstrap tabs without Bootstrap's JavaScript: TabContainer, TabLink and TabPane switch panes from state, with arrow keys and colored or vertical styles.

> This component is only available in **AdminKit PRO**. [Learn more](/pricing/#react).

Tabs split related content into panes and show one pane at a time, so a settings page or a project view does not turn into one long scroll. The user clicks a tab, or moves to it with the arrow keys, and its pane appears.

Three React components do the work: `TabContainer`, `TabLink` and `TabPane`. They keep the active tab in React state, so you need neither Bootstrap's JavaScript nor react-bootstrap. They are part of the PRO version of [AdminKit for React](/react/).

You still write the nav and the panes with Bootstrap's markup. A `TabLink` and a `TabPane` with the same `eventKey` belong together. `TabContainer` holds the active key. It renders no element of its own, so it does not change your markup. The components are in `src/components/bootstrap/tabs.tsx`.

## Example

```html
<div class="tab">
  <ul class="nav nav-tabs" role="tablist">
    <li class="nav-item" role="presentation">
      <a class="nav-link active" id="tabsProfileTab" data-bs-toggle="tab" href="#tabsProfile" role="tab" aria-controls="tabsProfile" aria-selected="true">Profile</a>
    </li>
    <li class="nav-item" role="presentation">
      <a class="nav-link" id="tabsSecurityTab" data-bs-toggle="tab" href="#tabsSecurity" role="tab" aria-controls="tabsSecurity" aria-selected="false" tabindex="-1">Security</a>
    </li>
    <li class="nav-item" role="presentation">
      <a class="nav-link" id="tabsBillingTab" data-bs-toggle="tab" href="#tabsBilling" role="tab" aria-controls="tabsBilling" aria-selected="false" tabindex="-1">Billing</a>
    </li>
  </ul>
  <div class="tab-content">
    <div class="tab-pane fade show active" id="tabsProfile" role="tabpanel" aria-labelledby="tabsProfileTab" tabindex="0">
      <p>Your name, photo and contact details.</p>
    </div>
    <div class="tab-pane fade" id="tabsSecurity" role="tabpanel" aria-labelledby="tabsSecurityTab" tabindex="0">
      <p>Your password and two-factor authentication.</p>
    </div>
    <div class="tab-pane fade" id="tabsBilling" role="tabpanel" aria-labelledby="tabsBillingTab" tabindex="0">
      <p>Your plan, invoices and payment method.</p>
    </div>
  </div>
</div>
```
```tsx code
import { TabContainer, TabLink, TabPane } from "@/components/bootstrap/tabs";

export function AccountTabs() {
  return (
    <div className="tab">
      <TabContainer defaultActiveKey="profile">
        <ul className="nav nav-tabs" role="tablist">
          <li className="nav-item" role="presentation">
            <TabLink eventKey="profile" className="nav-link">Profile</TabLink>
          </li>
          <li className="nav-item" role="presentation">
            <TabLink eventKey="security" className="nav-link">Security</TabLink>
          </li>
          <li className="nav-item" role="presentation">
            <TabLink eventKey="billing" className="nav-link">Billing</TabLink>
          </li>
        </ul>
        <div className="tab-content">
          <TabPane eventKey="profile">
            <p>Your name, photo and contact details.</p>
          </TabPane>
          <TabPane eventKey="security">
            <p>Your password and two-factor authentication.</p>
          </TabPane>
          <TabPane eventKey="billing">
            <p>Your plan, invoices and payment method.</p>
          </TabPane>
        </div>
      </TabContainer>
    </div>
  );
}
```

The `.tab` wrapper gives you AdminKit's tab style: borderless tabs on top of a card-like `.tab-content`. Leave it out to get Bootstrap's plain `.nav-tabs`. For pills, use `nav nav-pills` on the list.

## Props

`TabContainer`:

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `defaultActiveKey` | `string` | | The tab that is active first |
| `activeKey` | `string` | | Controlled: the active tab; use it with `onSelect` |
| `onSelect` | `(key: string) => void` | | Called with the `eventKey` of the tab that was selected |

`TabLink`:

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `eventKey` | `string` | | The key of the pane it shows |
| `as` | `ElementType` | `"a"` | The element: an `a` with `href="#"`, or `"button"` (gets `type="button"`) |
| `disabled` | `boolean` | | Adds `.disabled`; a click does nothing and the arrow keys skip it |

`TabPane`:

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `eventKey` | `string` | | The key of its tab |
| `fade` | `boolean` | `true` | Fades the pane in (`.fade`); `false` switches at once |

Other props, such as `className` and `onClick`, go to the `TabLink` and `TabPane` elements. You do not write the `id`, `aria-controls` and `aria-labelledby` attributes that connect a tab and its pane: they are generated with `useId`.

## Controlled tabs

Sometimes the active tab has to change from outside the nav: a button elsewhere on the page opens a tab, or the URL keeps track of it. Pass `activeKey` and `onSelect`, and the parent component owns the active tab:

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

export function SettingsTabs() {
  const [params, setParams] = useSearchParams();
  const tab = params.get("tab") ?? "profile";

  return (
    <TabContainer activeKey={tab} onSelect={(key) => setParams({ tab: key })}>
      {/* the nav and the panes */}
    </TabContainer>
  );
}
```

## Colored and vertical tabs

When the tabs should stand out, give them a color. Add a theme color to the wrapper (`tab-primary`, `tab-success`, `tab-danger`, ...) to color the active tab and the content. Add `tab-vertical` to put the tabs on the left. The demo panes use `.tab-title` for their headings.

```html
<div class="tab tab-vertical tab-primary">
  <ul class="nav nav-tabs" role="tablist">
    <li class="nav-item" role="presentation">
      <a class="nav-link active" id="tabsVerticalGeneralTab" data-bs-toggle="tab" href="#tabsVerticalGeneral" role="tab" aria-controls="tabsVerticalGeneral" aria-selected="true">General</a>
    </li>
    <li class="nav-item" role="presentation">
      <a class="nav-link" id="tabsVerticalTeamTab" data-bs-toggle="tab" href="#tabsVerticalTeam" role="tab" aria-controls="tabsVerticalTeam" aria-selected="false" tabindex="-1">Team</a>
    </li>
  </ul>
  <div class="tab-content">
    <div class="tab-pane active show" id="tabsVerticalGeneral" role="tabpanel" aria-labelledby="tabsVerticalGeneralTab" tabindex="0">
      <h2 class="h4 tab-title">General</h2>
      <p>The name and the time zone of the workspace.</p>
    </div>
    <div class="tab-pane" id="tabsVerticalTeam" role="tabpanel" aria-labelledby="tabsVerticalTeamTab" tabindex="0">
      <h2 class="h4 tab-title">Team</h2>
      <p>The members of the workspace and their roles.</p>
    </div>
  </div>
</div>
```
```tsx code
<div className="tab tab-vertical tab-primary">
  <TabContainer defaultActiveKey="general">
    <ul className="nav nav-tabs" role="tablist">
      <li className="nav-item" role="presentation">
        <TabLink eventKey="general" className="nav-link">General</TabLink>
      </li>
      <li className="nav-item" role="presentation">
        <TabLink eventKey="team" className="nav-link">Team</TabLink>
      </li>
    </ul>
    <div className="tab-content">
      <TabPane eventKey="general" fade={false}>
        <h2 className="h4 tab-title">General</h2>
        <p>The name and the time zone of the workspace.</p>
      </TabPane>
      <TabPane eventKey="team" fade={false}>
        <h2 className="h4 tab-title">Team</h2>
        <p>The members of the workspace and their roles.</p>
      </TabPane>
    </div>
  </TabContainer>
</div>
```

Tabs can also show an icon instead of a label. Put the icon in the `TabLink` and give the link an `aria-label`, as the demo does: `<TabLink eventKey="settings" class="nav-link" aria-label="Settings"><Settings class="align-middle" /></TabLink>`. The `.tab` style sizes the icon to 20px.

## Accessibility

- `TabLink` has `role="tab"`, `aria-selected` and `aria-controls`. `TabPane` has `role="tabpanel"`, `aria-labelledby` and `tabIndex={0}`. Add `role="tablist"` to the list and `role="presentation"` to its items yourself, as in the example.
- Only the active tab is in the Tab order. Arrow keys, Home and End move to another tab of the same `role="tablist"` and show its pane, as in Bootstrap 5.3. Without `role="tablist"` on the list, the arrow keys do nothing.
- Icon-only tabs need an `aria-label`.
- `disabled` only adds the `.disabled` class. Add `aria-disabled="true"` for screen readers.

## Related

- [Next.js Bootstrap tabs](/nextjs/docs/navigation/tabs/): the same component in AdminKit for Next.js
- [Bootstrap 5 navs and tabs](/bootstrap/docs/navigation/navs-tabs/): the nav and tab classes of the HTML version
- [Accordion](/react/docs/components/accordion/): another way to show one section at a time
- [Tabs demo](https://react-demo.adminkit.io/ui-tabs)

---

Canonical HTML page: https://adminkit.io/react/docs/navigation/tabs/

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
