Scroll area

A native scroll container with thin overlay scrollbars and edge fades that appear only when content overflows.

disclosurenew
Preview
Scroll area preview

Row 1. The scrollbar fades in when you scroll and fades out when you stop.

Row 2. The scrollbar fades in when you scroll and fades out when you stop.

Row 3. The scrollbar fades in when you scroll and fades out when you stop.

Row 4. The scrollbar fades in when you scroll and fades out when you stop.

Row 5. The scrollbar fades in when you scroll and fades out when you stop.

Row 6. The scrollbar fades in when you scroll and fades out when you stop.

Row 7. The scrollbar fades in when you scroll and fades out when you stop.

Row 8. The scrollbar fades in when you scroll and fades out when you stop.

Row 9. The scrollbar fades in when you scroll and fades out when you stop.

Row 10. The scrollbar fades in when you scroll and fades out when you stop.

Row 11. The scrollbar fades in when you scroll and fades out when you stop.

Row 12. The scrollbar fades in when you scroll and fades out when you stop.

When to use

  • Panels, sidebars, and popovers whose content can outgrow their box.
  • Horizontal strips of cards or chips that need mouse-wheel scrolling and snap points.
  • Places where platform scrollbars look heavy but native scrolling must be kept.

When not to use

  • Use carousel when items should page one at a time with controls.
  • Do not wrap the whole page; let the document scroll natively.
  • Use data-grid for large tabular data; it virtualizes its own scroll.

shadcn CLI

Also installs (1)

  • manicat-foundation
Manual install(+)
registry.jsonjson
{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "scroll-area",
  "type": "registry:ui",
  "title": "Scroll area",
  "description": "A native scroll container with thin overlay scrollbars and edge fades that appear only when content overflows.",
  "categories": [
    "disclosure"
  ],
  "dependencies": [],
  "registryDependencies": [
    "https://manicat.viraza.net/r/manicat-foundation.json"
  ],
  "files": [
    {
      "path": "registry/components/scroll-area/scroll-area.tsx",
      "type": "registry:component",
      "target": "@components/manicat/scroll-area/scroll-area.tsx"
    },
    {
      "path": "registry/components/scroll-area/scroll-area.module.css",
      "type": "registry:component",
      "target": "@components/manicat/scroll-area/scroll-area.module.css"
    }
  ],
  "docs": "Docs and live preview: https://uiarc.dev/components/scroll-area",
  "meta": {
    "tier": "free",
    "kind": "component",
    "docs": "https://manicat.viraza.net/components/scroll-area",
    "markdown": "https://uiarc.dev/components/scroll-area/markdown",
    "tags": [
      "disclosure",
      "new"
    ]
  }
}

Usage

Install the component, then copy this into your app. Change the content and the props to fit your screen.

example.tsxtsx
"use client";

import { ScrollArea } from "@/components/manicat/scroll-area/scroll-area";

export function ScrollAreaExample() {
  return (
    <ScrollArea maxHeight={180} className="w-full max-w-sm rounded-[var(--radius-panel)] border border-(color:--border) p-4">
      <div className="flex flex-col gap-3 text-sm">
        {Array.from({ length: 12 }, (_, index) => (
          <p key={index} className="text-(color:--text-muted)">
            Row {index + 1}. The scrollbar fades in when you scroll and fades out when you stop.
          </p>
        ))}
      </div>
    </ScrollArea>
  );
}

Examples

Real screens rather than isolated props. Switch to Code to copy a whole example into your project.

Activity log A bounded list with an overlay scrollbar that only shows while scrolling.

Activity log A bounded list with an overlay scrollbar that only shows while scrolling. preview

09:10 · Deployment #140 finished

09:13 · Deployment #141 finished

09:16 · Deployment #142 finished

09:19 · Deployment #143 finished

09:22 · Deployment #144 finished

09:25 · Deployment #145 finished

09:28 · Deployment #146 finished

09:31 · Deployment #147 finished

09:34 · Deployment #148 finished

09:37 · Deployment #149 finished

09:40 · Deployment #150 finished

09:43 · Deployment #151 finished

Keyboard interactions

KeysAction
TabThe viewport is focusable (tabIndex 0).
Arrow keys / Page Up / Page Down / Home / End / SpaceScroll natively once the viewport has focus.

Accessibility

  • Scrolling stays native, so keyboard, screen reader, and touch behavior match the platform.
  • With label, the viewport is a named region; without one, give it context another way.
  • The overlay tracks and thumbs are aria-hidden; the hidden native scrollbar remains the real control.

Motion

  • Scrollbars fade in while scrolling or on hover and fade out after hideDelay; the thumb thickens under the pointer.
  • Pressing the track pages 90% of the viewport toward the pointer with smooth scrolling, or instantly under reduced motion.
  • Reduced motion removes scrollbar transitions.

Responsive behavior

  • Touch keeps native momentum scrolling; the overlay bars only appear while scrolling.
  • Thumbs only thicken on hover-capable fine pointers.
  • Content changes and resizes are tracked with ResizeObserver and MutationObserver, so fades and thumbs stay correct as layouts reflow.

Performance

  • Fades and thumb positions are written straight to the DOM on each scroll event; scrolling never re-renders React.
  • Horizontal wheel conversion only captures the wheel while there is room to scroll, so the page still scrolls at the ends.
  • Edge fades use CSS mask-image, which is cheap but still composited; set fade={0} in very long lists if you see cost.

Notes for AI

  • Use it wherever content scrolls inside a fixed box: sidebars, panels, menus, horizontal card strips.
  • Give vertical areas a height or maxHeight; the viewport scrolls only when it has a constrained size.
  • Use onEdgeChange to show a "more below" affordance or to load more when bottom becomes false.