---
title: Drag Scroll Area
description: Makes an overflowing horizontal strip drag-scrollable, with alpha edge fades and keyboard-focus-into-view.
icon: move-horizontal
---

```package-install
npx shadcn@latest add @redpanda/drag-scroll-area
```

<ComponentPreview name="drag-scroll-area-demo" description="A filter toolbar that overflows its container, made drag-scrollable." />

## When to use

Wrap any **single-row** content that can overflow its container horizontally — a tab strip, a
filter toolbar, a chip row — to make it draggable instead of clipping or forcing a wrap onto
multiple lines. It is the primitive used to make [`Tabs`](/docs/tabs#scrollable) strips scrollable.

Reach for it when:

- The number of items is variable and a fixed single-row height matters (no layout shift from wrapping).
- You want a pointer-draggable strip without a visible scrollbar.

For vertical scrolling or a styled cross-browser scrollbar, use [`ScrollArea`](/docs/scroll-area) instead.

## Behavior

- **Drag to scroll** — click and drag the strip sideways. A real drag (more than 4px) swallows the
  trailing click, so dragging never accidentally activates the item under the pointer.
- **Alpha edge fades** — the side(s) with hidden content fade out via a CSS mask over the content
  itself. Because it masks rather than overlays a color, it blends on any background and needs no
  theming. The fade only appears on edges that actually overflow.
- **Preserve the bottom edge** — set `preserveBottomEdge` to keep the bottom N px fully opaque
  (e.g. an underline tab strip's baseline border) while the labels above it still fade. It unions a
  second mask layer with `mask-composite: add`, so a pixel survives if either layer keeps it.
- **Keyboard focus** — when any focusable descendant receives focus (roving tab focus, arrow keys,
  programmatic focus), it is scrolled fully clear of the edge fade. This is generic: it reacts to
  `focusin` and has no knowledge of its children.
- **Grab cursor** — shown only while there is something to scroll.

## Usage

```tsx
import { DragScrollArea } from '@/components/drag-scroll-area';

<DragScrollArea>
  <div className="flex w-max gap-2">{/* single-row content */}</div>
</DragScrollArea>;
```

The child should size to its content (e.g. `w-max`) so it can overflow rather than shrink to fit.

## Props

`DragScrollArea` accepts all native `div` props, plus:

| Prop                | Type     | Default | Description                                                                 |
| ------------------- | -------- | ------- | --------------------------------------------------------------------------- |
| `fadeSize`          | `number` | `32`    | Width of the edge fade in px, and the inset a focused child is scrolled clear of. |
| `preserveBottomEdge`| `number` | `0`     | Keep the bottom N px fully opaque, exempt from the edge fade (e.g. an underline baseline). |
| `testId`            | `string` | —       | Sets `data-testid` on the scroll container for testing.                     |

## Accessibility

- Focusing a descendant always scrolls it fully into view, so keyboard users never land on a
  clipped item.
- The edge fade snaps in and out (no transition), so it carries no motion for reduced-motion users.
- Horizontal only by design — do not use it for vertical overflow.
