# ScrollArea

> A region with native scrolling and a thin drawn scrollbar that shows only while it scrolls, for lists and strips inside a fixed frame.

Source: https://raft-design.botiverse.build/components/scroll-area. Generated from the current release; where it differs from what you remember, this page is right.

ScrollArea scrolls its content natively and draws a thin scrollbar over the edge that shows only while the content moves, so the scrollbar takes no room from the layout. raft-ui uses it for the desktop message list, the sidebar and the composer's row of attachments.

```tsx
import {
  ScrollArea,
  ScrollAreaContent,
  ScrollAreaScrollbar,
  ScrollAreaThumb,
  ScrollAreaViewport,
  Text,
} from "@botiverse/raft-ui";

export function ScrollAreaBasicComposition() {
  return (
    <ScrollArea className="h-56 w-72 border border-line-hairline bg-layer-canvas">
      <ScrollAreaViewport>
        <ScrollAreaContent className="p-4">
          <Text.Sans size="small">
            Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor
            incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud
            exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure
            dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur.
            Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt
            mollit anim id est laborum. Curabitur pretium tincidunt lacus. Nulla gravida orci a
            odio. Nullam varius, turpis et commodo pharetra, est eros bibendum elit, nec luctus
            magna felis sollicitudin mauris. Integer in mauris eu nibh euismod gravida. Duis ac
            tellus et risus vulputate vehicula. Donec lobortis risus a elit. Etiam tempor. Ut
            ullamcorper, ligula eu tempor congue, eros est euismod turpis, id tincidunt sapien risus
            a quam. Maecenas fermentum consequat mi. Donec fermentum. Pellentesque malesuada nulla a
            mi. Duis sapien sem, aliquet nec, commodo eget, consequat quis, neque.
          </Text.Sans>
        </ScrollAreaContent>
      </ScrollAreaViewport>
      <ScrollAreaScrollbar>
        <ScrollAreaThumb />
      </ScrollAreaScrollbar>
    </ScrollArea>
  );
}
```

## When to use

| Situation                                         | Use                                                                          |
| ------------------------------------------------- | ---------------------------------------------------------------------------- |
| A list or strip that scrolls inside a fixed frame | ScrollArea                                                                   |
| The content of a panel under its header           | `PanelScrollViewport` ([Panel](https://raft-design.botiverse.build/components/panel.md#anatomy))                   |
| The messages of a conversation or a thread        | [MessageList](https://raft-design.botiverse.build/components/message-list.md), which is built on ScrollArea |
| A row of tabs that doesn't fit                    | [Tabs](https://raft-design.botiverse.build/components/tabs.md#behavior), which scrolls on its own                   |
| A Markdown table wider than the text              | `MarkdownTableScroll` ([Markdown](https://raft-design.botiverse.build/components/markdown.md))                  |

## Anatomy

- `ScrollArea`: the frame. Give it the size of the region. Required.
- `ScrollAreaViewport`: the element that scrolls. Required.
- `ScrollAreaContent`: the content, at least as tall as the viewport. Required.
- `ScrollAreaScrollbar`: a track over one edge, vertical by default. Add a second with `orientation="horizontal"` for content that scrolls both ways.
- `ScrollAreaThumb`: the 6px handle in the track, in translucent ink. Required inside each scrollbar.
- `ScrollAreaCorner`: the square where two scrollbars meet. Only with both.

```tsx
import {
  ScrollArea,
  ScrollAreaContent,
  ScrollAreaCorner,
  ScrollAreaScrollbar,
  ScrollAreaThumb,
  ScrollAreaViewport,
  Text,
} from "@botiverse/raft-ui";

const CELLS = Array.from({ length: 12 }, (_, row) =>
  Array.from({ length: 12 }, (_, column) => `${row + 1}:${column + 1}`),
);

export function ScrollAreaBothAxesComposition() {
  return (
    <ScrollArea className="h-56 w-72 border border-line-hairline bg-layer-canvas">
      <ScrollAreaViewport>
        <ScrollAreaContent className="p-4">
          <div className="grid w-[36rem] grid-cols-12 gap-2">
            {CELLS.flatMap((row) =>
              row.map((cell) => (
                <Text.Sans
                  key={cell}
                  size="small"
                  className="flex size-12 items-center justify-center bg-fill-muted"
                >
                  {cell}
                </Text.Sans>
              )),
            )}
          </div>
        </ScrollAreaContent>
      </ScrollAreaViewport>
      <ScrollAreaScrollbar>
        <ScrollAreaThumb />
      </ScrollAreaScrollbar>
      <ScrollAreaScrollbar orientation="horizontal">
        <ScrollAreaThumb />
      </ScrollAreaScrollbar>
      <ScrollAreaCorner />
    </ScrollArea>
  );
}
```

## States

| State       | Look                                                                |
| ----------- | ------------------------------------------------------------------- |
| At rest     | No scrollbar. The track doesn't catch the pointer                   |
| Scrolling   | The scrollbar shows at once and fades out after the scrolling stops |
| Thumb hover | The thumb darkens                                                   |

## Behavior

- Scrolling is the browser's own: wheel, trackpad, touch and keyboard scroll the viewport as usual.
- Scrolling stops at the end of the area and doesn't carry on to the page behind it.
- The viewport keeps the visible content in place when content above it changes size.

## Accessibility

- ScrollArea wraps Base UI `ScrollArea`. The viewport becomes focusable while its content overflows, so keyboard users can scroll it with the arrow keys, Page Up and Page Down.
- The viewport has no role of its own. Name and structure the content inside it, such as a list with an `aria-label`.

## Rules

- Give each region one scroll container: the header stays put and only the content scrolls ([Panel](https://raft-design.botiverse.build/components/panel.md#behavior)).
- Size the frame, not the content: a ScrollArea without a height grows with its content and never scrolls.

## API

### ScrollArea

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `overflowEdgeThreshold` | `number \| Partial<{ xStart: number; xEnd: number; yStart: number; yEnd: number; }>` | `0` | The threshold in pixels that must be passed before the overflow edge attributes are applied. Accepts a single number for all edges or an object to configure them individually. |
| `render` | `ReactElement \| ((props, state) => ReactElement)` |  | Allows you to replace the component's HTML element with a different tag, or compose it with another component. |

Also takes the attributes of its element.

### ScrollAreaContent

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `render` | `ReactElement \| ((props, state) => ReactElement)` |  | Allows you to replace the component's HTML element with a different tag, or compose it with another component. |

Also takes the attributes of its element.

### ScrollAreaCorner

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `render` | `ReactElement \| ((props, state) => ReactElement)` |  | Allows you to replace the component's HTML element with a different tag, or compose it with another component. |

Also takes the attributes of its element.

### ScrollAreaScrollbar

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `keepMounted` | `boolean` | `false` | Whether to keep the HTML element in the DOM when the viewport isn't scrollable. |
| `orientation` | `"horizontal" \| "vertical"` | `"vertical"` | Whether the scrollbar controls vertical or horizontal scroll. |
| `render` | `ReactElement \| ((props, state) => ReactElement)` |  | Allows you to replace the component's HTML element with a different tag, or compose it with another component. |

Also takes the attributes of its element.

### ScrollAreaThumb

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `render` | `ReactElement \| ((props, state) => ReactElement)` |  | Allows you to replace the component's HTML element with a different tag, or compose it with another component. |

Also takes the attributes of its element.

### ScrollAreaViewport

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `render` | `ReactElement \| ((props, state) => ReactElement)` |  | Allows you to replace the component's HTML element with a different tag, or compose it with another component. |

Also takes the attributes of its element.

## Changes

- 0.5.53 (Fixed): `MessageList` no longer lets wide content (a `w-max` tab strip, a long code line) stretch every message past a narrow list on a desktop viewport: its `ScrollAreaContent` resets Base UI's inline `min-width: fit-content` to 0, so the content keeps the list width and wide children scroll inside their own box.
- 0.1.0 (Added): Added `ConversationPanel`, `ThreadPanel`, `Panel`, `ScrollArea`, `FilePreview`, `RaftLogo` / lockup / wordmark, and `MobileNav`.
