Skip to content
Redpanda UI
Esc
↑↓navigate↵open⌘Jpreview
On this page

Drag Scroll Area

Makes an overflowing horizontal strip drag-scrollable, with alpha edge fades and keyboard-focus-into-view.

npx shadcn@latest add @redpanda/drag-scroll-area
pnpm dlx shadcn@latest add @redpanda/drag-scroll-area
yarn dlx shadcn@latest add @redpanda/drag-scroll-area
bunx shadcn@latest add @redpanda/drag-scroll-area
nubx shadcn@latest add @redpanda/drag-scroll-area
aube dlx shadcn@latest add @redpanda/drag-scroll-area

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 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 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

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.

Recent changes

  • patchv1.2.0Pin shipped dependency floors to the version we develop against. Registry items now declare ranges like `^5.1.9` (the actual installed version) instead of collapsing to `^5.0.0`, so consumers start on the known-tested baseline while caret semantics still allow any compatible release within the same major.#133
  • minorv1.1.0Theme docs refresh, readability pass on semantic foregrounds, and consumer-facing Base UI regression fixes.#121
  • minorv1.0.0Post-Base-UI polish. Public API unchanged.#116
  • majorv1.0.0Migrate every Radix-based primitive to `@base-ui/react@^1.4.0` (Base UI).#114
  • minorv0.3.0Add theme-provider component to the registry with documentation and tests. Includes playground type improvements (export RegistryItem, remove as-const boilerplate) and docs site dark mode border color fix.#109
See full history →

Last updated on October 7, 2026

Was this page helpful?