# Next.js Bootstrap Tooltips

A Next.js Bootstrap tooltip for the App Router: wrap an element in a client component for a tooltip on hover and focus, positioned by Floating UI.

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

Tooltips give a short hint about an element when the user hovers over it or moves the focus to it: what an icon button does, or a detail that does not fit in the label. `Tooltip` wraps one element and adds Bootstrap's tooltip to it. Floating UI positions the tooltip and Bootstrap's CSS styles it, without Bootstrap's JavaScript or Popper. Tooltips are part of the PRO version of [AdminKit for Next.js](/nextjs/).

In the App Router, it matters where you write the tooltip. `Tooltip` is a client component, a file that starts with `"use client"`. It clones its child element to attach its handlers and `ref`. Pages are server components, though: they render on the server. An element that a server component creates reaches the client component in a form it cannot clone. So `Tooltip` throws an error there: "Tooltip and Popover clone their child element: create it in a client component". Put the `Tooltip` and its child in a client component, and render that from your page.

## Example

```html
<button type="button" class="btn btn-secondary me-2 mb-2" data-bs-toggle="tooltip" data-bs-placement="left" data-bs-title="Tooltip on left">Tooltip on left</button>
<button type="button" class="btn btn-secondary me-2 mb-2" data-bs-toggle="tooltip" data-bs-placement="top" data-bs-title="Tooltip on top">Tooltip on top</button>
<button type="button" class="btn btn-secondary me-2 mb-2" data-bs-toggle="tooltip" data-bs-placement="bottom" data-bs-title="Tooltip on bottom">Tooltip on bottom</button>
<button type="button" class="btn btn-secondary me-2 mb-2" data-bs-toggle="tooltip" data-bs-placement="right" data-bs-title="Tooltip on right">Tooltip on right</button>
```
```tsx code title="src/components/tooltip-buttons.tsx"
"use client";

import { Tooltip } from "@/components/bootstrap/overlay";

const placements = ["left", "top", "bottom", "right"] as const;

export function TooltipButtons() {
  return placements.map((placement) => (
    <Tooltip key={placement} content={`Tooltip on ${placement}`} placement={placement}>
      <button type="button" className="btn btn-secondary me-2 mb-2">
        Tooltip on {placement}
      </button>
    </Tooltip>
  ));
}
```

The page stays a server component:

```tsx title="src/app/(dashboard)/ui-general/page.tsx"
import { TooltipButtons } from "@/components/tooltip-buttons";
import { pageMetadata } from "@/lib/site";

export const metadata = pageMetadata("/ui-general");

export default function Page() {
  return (
    <div className="card">
      <div className="card-body">
        <TooltipButtons />
      </div>
    </div>
  );
}
```

The server HTML has the buttons only. The tooltip renders in a portal on `document.body` after hydration, once the browser has taken over that HTML. That way the server and the browser agree on the markup. When the chosen side has no room, the tooltip flips to the other side. It stays 8px from the edges of the window.

## Props

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `content` | `ReactNode` | | The text of `.tooltip-inner` |
| `placement` | `"top" \| "bottom" \| "left" \| "right"` | `"top"` | The side of the element it appears on |
| `trigger` | `("hover" \| "focus" \| "click")[]` | `["hover", "focus"]` | The events that show it |
| `children` | `ReactElement` | | One element, created in a client component |

The child gets a `ref`, the trigger handlers and `aria-describedby`. It must be an HTML element or a component that passes `ref` and the other props on to one. Its own handlers, such as `onClick`, keep working. The component is in `src/components/bootstrap/overlay.tsx`.

## HTML content

When plain text is not enough, pass markup. `content` is a React node, so it can hold formatting:

```html
<button type="button" class="btn btn-secondary" data-bs-toggle="tooltip" data-bs-html="true" data-bs-title="<em>Tooltip</em> <u>with</u> <b>HTML</b>">Tooltip with HTML</button>
```
```tsx code
<Tooltip
  content={
    <>
      <em>Tooltip</em> <u>with</u> <b>HTML</b>
    </>
  }
>
  <button type="button" className="btn btn-secondary">Tooltip with HTML</button>
</Tooltip>
```

## Disabled elements

A tooltip is a good place to say why a button is disabled. But a disabled button receives no mouse or focus events, so put the tooltip on a focusable `span` around it:

```html
<span class="d-inline-block" tabindex="0" data-bs-toggle="tooltip" data-bs-title="Upgrade to export">
  <button type="button" class="btn btn-primary" disabled>Export CSV</button>
</span>
```
```tsx code
<Tooltip content="Upgrade to export">
  <span className="d-inline-block" tabIndex={0}>
    <button type="button" className="btn btn-primary" disabled>Export CSV</button>
  </span>
</Tooltip>
```

## Accessibility

- The tooltip has `role="tooltip"`. While it shows, the element has `aria-describedby` pointing at it, so screen readers read it as the element's description.
- A description does not replace a name: an icon-only button still needs an `aria-label`.
- The tooltip shows on focus, so keyboard users get it too, as long as the element can take the focus (a button, a link or `tabIndex={0}`). Escape hides it.
- Keep it to short text. It hides when the pointer leaves the element, so links in it cannot be reached. Use a [popover](/nextjs/docs/components/popovers/) instead.

## Related

- [React Bootstrap tooltips](/react/docs/components/tooltips/): the same component in AdminKit for React (Vite)
- [Bootstrap 5 tooltips](/bootstrap/docs/components/tooltips/): the tooltips of the HTML version
- [Popovers](/nextjs/docs/components/popovers/): a larger overlay that opens on click
- [General UI demo](https://nextjs-demo.adminkit.io/ui-general)

---

Canonical HTML page: https://adminkit.io/nextjs/docs/components/tooltips/

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
