> Leaf UI 0.8.1 · Package: @ifhover/leaf-ui · Source: 435bab23cad7d343653d9b63b049c0b0601eeefb
> Browser documentation: https://ifhover.github.io/leaf-ui/en/components/scrollbar.html

> For AI agents: the complete documentation index is available at https://ifhover.github.io/leaf-ui/en/llms.txt, the full documentation bundle is available at https://ifhover.github.io/leaf-ui/en/llms-full.txt.

# Scrollbar

Browse long or wide content inside a region.

## Basic usage

Scrollbars fade in when the pointer enters the region and fade out when it leaves, without taking up content space. Wheel, touch and keyboard scrolling retain native behavior.


Project activity 1

Project activity 2

Project activity 3

Project activity 4

Project activity 5

Project activity 6

Project activity 7

Project activity 8

Project activity 9

Project activity 10

Project activity 11

Project activity 12

basic.tsx

Copy code

```tsx lineNumbers file="../../../src/examples/scrollbar/basic.tsx"
import { Scrollbar } from '@ifhover/leaf-ui';

export function ScrollbarBasic() {
  return (
    <Scrollbar height={200} aria-label={'Project activity'}>
      {Array.from({ length: 12 }, (_, index) => (
        <div key={String(index)} style={{ padding: '12px 20px' }}>
          {'Project activity'} {index + 1}
        </div>
      ))}
    </Scrollbar>
  );
}
```

Expand code

## Horizontal and always visible

`orientation` controls the scrolling axes. Use `scrollbar="always"` to keep bars visible, or `hidden` to hide them without disabling scrolling. Drag a thumb or click its track to move through the content.


Project 1

Project 2

Project 3

Project 4

Project 5

Project 6

basic.tsx

Copy code

```tsx lineNumbers file="../../../src/examples/scrollbar/basic.tsx"
import { Scrollbar } from '@ifhover/leaf-ui';

export function ScrollbarHorizontal() {
  return (
    <Scrollbar
      orientation="horizontal"
      scrollbar="always"
      aria-label={'Project board'}
    >
      <div style={{ display: 'flex', width: 'max-content', gap: 16, padding: '12px 12px 24px' }}>
        {Array.from({ length: 6 }, (_, index) => (
          <div
            key={String(index)}
            style={{
              width: 180,
              padding: 24,
              borderRadius: 'var(--leaf-radius)',
              background: 'var(--leaf-color-surface-muted)',
            }}
          >
            {'Project'} {index + 1}
          </div>
        ))}
      </div>
    </Scrollbar>
  );
}
```

Expand code

## Dynamic content and scroll control

Thumbs update when content or container sizes change. The forwarded `ref` points to the scrolling viewport: call `scrollTo`, read `scrollTop`, or use it with BackTop. At a scroll boundary, or when content fits, the wheel continues scrolling the page.


Add itemsBack to top

Item 1

Item 2

Item 3

basic.tsx

Copy code

```tsx lineNumbers file="../../../src/examples/scrollbar/basic.tsx"
import { Button, Flex, Scrollbar } from '@ifhover/leaf-ui';

import { useRef, useState } from 'react';

export function ScrollbarDynamic() {
  const viewport = useRef<HTMLDivElement>(null);
  const [count, setCount] = useState(3);
  return (
    <Flex direction="column" gap="md">
      <Flex gap="sm" wrap>
        <Button variant="outline" onClick={() => setCount((previous) => previous + 3)}>
          {'Add items'}
        </Button>
        <Button
          variant="soft"
          onClick={() => viewport.current?.scrollTo({ top: 0, behavior: 'smooth' })}
        >
          {'Back to top'}
        </Button>
      </Flex>
      <Scrollbar ref={viewport} maxHeight={200} aria-label={'Dynamic list'}>
        {Array.from({ length: count }, (_, index) => (
          <div key={String(index)} style={{ padding: '12px 20px' }}>
            {'Item'} {index + 1}
          </div>
        ))}
      </Scrollbar>
    </Flex>
  );
}
```

Expand code

## Scrollbar API

```ts
import type { ScrollbarProps } from '@ifhover/leaf-ui';
```

| Property            | Type                                   | Default  | Description                                                      |
| ------------------- | -------------------------------------- | -------- | ---------------------------------------------------------------- |
| `height`            | `CSSProperties['height']`              | —        | Viewport height.                                                 |
| `maxHeight`         | `CSSProperties['maxHeight']`           | —        | Maximum height; unconstrained content determines its own height. |
| `orientation`       | `'vertical' \| 'horizontal' \| 'both'` | `'both'` | Scrolling axes.                                                  |
| `scrollbar`         | `'auto' \| 'always' \| 'hidden'`       | `'auto'` | Scrollbar visibility.                                            |
| `viewportClassName` | `string`                               | —        | Class for the scrolling viewport.                                |
| `viewportStyle`     | `CSSProperties`                        | —        | Styles for the scrolling viewport.                               |
| `contentClassName`  | `string`                               | —        | Class for the content container.                                 |
| `contentStyle`      | `CSSProperties`                        | —        | Styles for the content container.                                |
| `verticalOffset`    | `number`                               | `0`      | Top inset for the vertical track, useful with sticky headers.    |

`style` and `className` apply to the outer wrapper. `id`, `role`, `tabIndex`, region names and `onScroll` apply to the scrolling viewport. Provide `aria-label` or `aria-labelledby` to name it. Focus a thumb with Tab and use arrow keys, PageUp / PageDown or Home / End.

`ScrollArea` uses the same scrollbar implementation and defaults to vertical scrolling. Existing usage remains supported.
