# Scroll To Bottom Button

Scroll To Bottom Button [#scroll-to-bottom-button]

The **ScrollToBottomButton** is a floating scroll-to-bottom affordance tied to a chat container’s scroll state. It appears when the user scrolls up from the bottom and scrolls to + pins the latest content on click. Pass a `containerRef`, or let it find the nearest scrollable ancestor. `ScrollAnchor` is the zero-height pin element you place at the end of the message list. Data source: `useChat` (token-stream growth).

Preview [#preview]

```tsx
'use client';

/**
 * @file scroll-to-bottom-button-demo.tsx
 * @description Docs preview for `ScrollToBottomButton`. Scroll up in the list to
 * reveal the floating button; click it to scroll back to the pinned anchor.
 */
import * as React from 'react';
import {
  ScrollAnchor,
  ScrollToBottomButton,
} from '@/components/scroll-to-bottom-button';

export default function ScrollToBottomButtonDemo() {
  const ref = React.useRef<HTMLDivElement>(null);
  return (
    <div className="relative h-72 w-full max-w-md overflow-hidden rounded-lg border border-border">
      <div ref={ref} className="h-full overflow-y-auto p-3">
        <div className="space-y-2">
          {Array.from({ length: 20 }).map((_, i) => (
            <div
              key={i}
              className="rounded-md border border-border bg-card px-3 py-2 text-sm text-card-foreground"
            >
              Message {i + 1}
            </div>
          ))}
          <ScrollAnchor />
        </div>
      </div>
      <ScrollToBottomButton containerRef={ref} />
    </div>
  );
}
```

Installation [#installation]

```bash
npx shadcn@latest add @localmode/ui/conversation/scroll-to-bottom-button
```

Data source & dependencies [#data-source--dependencies]

**Data source:** observes a scroll container you pass — works with any backend. Recommended producer: the streaming chat container fed by `useChat` from `@localmode/react` (on-device, optional).

* `clsx` + `tailwind-merge` — via the shared `cn()` util (installed automatically as a registry dependency)

Files installed [#files-installed]

* `scroll-to-bottom-button.tsx` — `ScrollToBottomButton` + `ScrollAnchor`
* `lib/utils.ts` — the `cn()` helper (if not already present)

Props [#props]

**ScrollToBottomButton**

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `containerRef` | `object` | — | The scroll container to observe. When omitted, the nearest scrollable ancestor of the button is used. |
| `threshold` | `number` | `24` | Distance (px) from the bottom under which the button hides. |
| `variant` | `"ghost" \| "secondary" \| "outline" \| "destructive" \| "default" \| "link" \| null` | — | — |
| `size` | `"icon-lg" \| "icon-sm" \| "icon-xs" \| "icon" \| "lg" \| "sm" \| "xs" \| "default" \| null` | — | — |
| `asChild` | `boolean` | — | — |

Examples [#examples]

Attach to a scroll container [#attach-to-a-scroll-container]

```tsx
import { ScrollAnchor, ScrollToBottomButton } from '@/components/scroll-to-bottom-button';

<div className="relative">
  <div ref={scrollRef} className="overflow-y-auto">
    {messages.map((m) => <Bubble key={m.id} {...m} />)}
    <ScrollAnchor />
  </div>
  <ScrollToBottomButton containerRef={scrollRef} />
</div>
```

Customization [#customization]

This is the standalone counterpart to the built-in `ConversationScrollButton`; use it when your chat surface is not the `Conversation` container. Adjust the `threshold` for how far up the user must scroll before it appears.

These primitives are presentational and hook-driven: they render props and emit callbacks, holding only local view state. The orchestration state (e.g. `useChat`) lives in your app. Every surface uses shadcn/ui CSS-variable utilities, so it inherits your theme — restyle the copied file freely.