The Scroll Area component provides a custom-styled scrollable container that maintains consistent appearance across different operating systems and browsers. It supports vertical, horizontal, or bidirectional scrolling.
Installation
npx shadcn@latest add @eo-n/scroll-area
Copy the component code
Copy and paste the Scroll Area component code into your project at components/ui/scroll-area.tsx.components/ui/scroll-area.tsx
"use client";
import * as React from "react";
import { ScrollArea as ScrollAreaPrimitive } from "@base-ui/react";
import { cn } from "@/lib/utils";
function ScrollArea({
scrollbars = "vertical",
className,
children,
...props
}: React.ComponentProps<typeof ScrollAreaPrimitive.Root> & {
scrollbars?: "vertical" | "horizontal" | "both";
}) {
return (
<ScrollAreaPrimitive.Root
data-slot="scroll-area"
className={cn("relative flex min-h-0 overflow-hidden", className)}
{...props}
>
<ScrollAreaPrimitive.Viewport
className={cn(
"focus-visible:ring-ring/50 min-h-0 flex-1 overscroll-contain rounded-[inherit] p-3 transition-[color,box-shadow] outline-none focus-visible:ring-[3px] focus-visible:outline-1",
scrollbars === "vertical" && "overflow-y-auto",
scrollbars === "horizontal" && "overflow-x-auto",
scrollbars === "both" && "overflow-auto"
)}
>
<ScrollAreaPrimitive.Content>{children}</ScrollAreaPrimitive.Content>
</ScrollAreaPrimitive.Viewport>
{scrollbars === "vertical" && (
<ScrollAreaScrollbar orientation="vertical" />
)}
{scrollbars === "horizontal" && (
<ScrollAreaScrollbar orientation="horizontal" />
)}
{scrollbars === "both" && (
<>
<ScrollAreaScrollbar orientation="vertical" />
<ScrollAreaScrollbar orientation="horizontal" />
</>
)}
<ScrollAreaCorner />
</ScrollAreaPrimitive.Root>
);
}
function ScrollAreaScrollbar({
className,
orientation = "vertical",
...props
}: React.ComponentProps<typeof ScrollAreaPrimitive.Scrollbar>) {
return (
<ScrollAreaPrimitive.Scrollbar
data-slot="scroll-area-scrollbar"
orientation={orientation}
className={cn(
"m-0.5 touch-none rounded opacity-0 transition-opacity delay-300 select-none data-[hovering]:opacity-100 data-[hovering]:delay-0 data-[hovering]:duration-75 data-[scrolling]:opacity-100 data-[scrolling]:delay-0 data-[scrolling]:duration-75",
orientation === "vertical" && "w-2",
orientation === "horizontal" && "h-2",
className
)}
{...props}
>
<ScrollAreaPrimitive.Thumb className="bg-border size-full rounded-full" />
</ScrollAreaPrimitive.Scrollbar>
);
}
function ScrollAreaCorner({
className,
...props
}: React.ComponentProps<typeof ScrollAreaPrimitive.Corner>) {
return (
<ScrollAreaPrimitive.Corner
data-slot="scroll-area-corner"
className={cn(className)}
{...props}
/>
);
}
export { ScrollArea, ScrollAreaScrollbar, ScrollAreaCorner };
Update imports
Update the import paths to match your project setup.
Usage
Import the Scroll Area component:
import { ScrollArea } from "@/components/ui/scroll-area";
<ScrollArea className="h-20 w-50 rounded-md border">
<p>
The ScrollArea component allows for overflow content to be easily scrollable
while maintaining a clean and responsive UI. It is particularly useful when
dealing with long lists, articles, or any content that exceeds the
designated container height.
</p>
</ScrollArea>
Component API
ScrollArea
The main scrollable container component.
scrollbars
'vertical' | 'horizontal' | 'both'
default:"vertical"
Which scrollbars to display.
Additional CSS classes to apply to the container.
ScrollAreaScrollbar
Custom scrollbar component (automatically rendered based on scrollbars prop).
orientation
'vertical' | 'horizontal'
default:"vertical"
The orientation of the scrollbar.
Additional CSS classes to apply to the scrollbar.
ScrollAreaCorner
Corner element that appears when both scrollbars are visible.
Additional CSS classes to apply to the corner.
Examples
Vertical Scrolling (Default)
<ScrollArea className="h-72 w-48 rounded-md border p-4">
<div className="space-y-4">
{Array.from({ length: 50 }).map((_, i) => (
<div key={i} className="text-sm">
Item {i + 1}
</div>
))}
</div>
</ScrollArea>
Horizontal Scrolling
<ScrollArea scrollbars="horizontal" className="w-96 whitespace-nowrap rounded-md border">
<div className="flex gap-4 p-4">
{Array.from({ length: 20 }).map((_, i) => (
<div key={i} className="inline-block w-32 h-32 bg-muted rounded-md" />
))}
</div>
</ScrollArea>
Both Scrollbars
<ScrollArea scrollbars="both" className="h-72 w-96 rounded-md border">
<div className="min-w-max p-4">
<table className="w-full">
<thead>
<tr>
<th>Column 1</th>
<th>Column 2</th>
<th>Column 3</th>
<th>Column 4</th>
<th>Column 5</th>
</tr>
</thead>
<tbody>
{Array.from({ length: 50 }).map((_, i) => (
<tr key={i}>
<td>Row {i + 1} Col 1</td>
<td>Row {i + 1} Col 2</td>
<td>Row {i + 1} Col 3</td>
<td>Row {i + 1} Col 4</td>
<td>Row {i + 1} Col 5</td>
</tr>
))}
</tbody>
</table>
</div>
</ScrollArea>
Long Content
<ScrollArea className="h-96 w-full rounded-md border p-4">
<article className="prose dark:prose-invert">
<h2>Understanding Component Architecture</h2>
<p>
The scroll area component provides a consistent scrolling experience
across different browsers and platforms. It uses custom styled scrollbars
that match your design system while maintaining accessibility.
</p>
<p>
This component is particularly useful for displaying long content in a
constrained space, such as terms of service, privacy policies, or
documentation pages that need to fit within a modal or sidebar.
</p>
</article>
</ScrollArea>
List of Items
<ScrollArea className="h-60 w-64 rounded-md border">
<div className="p-4">
<h4 className="mb-4 text-sm font-medium leading-none">Tags</h4>
{tags.map((tag) => (
<>
<div key={tag} className="text-sm py-2">
{tag}
</div>
<Separator />
</>
))}
</div>
</ScrollArea>
With Custom Styling
<ScrollArea className="h-[450px] w-full rounded-md border bg-muted/50">
<div className="p-6 space-y-4">
{/* Your content */}
</div>
</ScrollArea>
Styling
The scrollbar appearance is customizable through CSS classes:
- Scrollbars fade in/out on hover and scroll
- Custom thumb color via
bg-border class
- Rounded scrollbar tracks and thumbs
- Configurable width (vertical) and height (horizontal)
Accessibility
The Scroll Area component is built on Base UI’s accessible ScrollArea primitive, which:
- Maintains native scrolling behavior and keyboard support
- Preserves focus management
- Works with screen readers
- Respects reduced motion preferences
- Supports touch and mouse interactions
For more details, see the Base UI Scroll Area documentation.