Skip to content

Instantly share code, notes, and snippets.

@devhammed
Last active December 14, 2025 18:48
Show Gist options
  • Select an option

  • Save devhammed/6c9bf2805d63d9222ae80da2d36ca2af to your computer and use it in GitHub Desktop.

Select an option

Save devhammed/6c9bf2805d63d9222ae80da2d36ca2af to your computer and use it in GitHub Desktop.
Shadcn UI Scroll Area with Overflow Fading Support
import { cn } from '@/lib/utils';
import * as ScrollAreaPrimitive from '@radix-ui/react-scroll-area';
import * as React from 'react';
import { useEffect, useRef, useState } from 'react';
const ScrollArea = React.forwardRef<
React.ComponentRef<typeof ScrollAreaPrimitive.Root>,
React.ComponentPropsWithoutRef<typeof ScrollAreaPrimitive.Root> & {
orientation?: 'horizontal' | 'vertical';
fade?: boolean;
}
>(({ className, children, fade = false, orientation = 'vertical', ...props }, ref) => {
const viewPortRef = useRef<HTMLDivElement | null>(null);
const [fadeState, setFadeState] = useState({
top: false,
bottom: false,
left: false,
right: false,
});
useEffect(() => {
if (!fade) {
return;
}
const el = viewPortRef.current;
if (!el) {
return;
}
const handleScroll = () => {
if (orientation === 'vertical') {
setFadeState({
top: el.scrollTop > 0,
bottom: el.scrollTop + el.clientHeight < el.scrollHeight,
left: false,
right: false,
});
} else {
setFadeState({
top: false,
bottom: false,
left: el.scrollLeft > 0,
right: el.scrollLeft + el.clientWidth < el.scrollWidth,
});
}
};
handleScroll();
el.addEventListener('scroll', handleScroll);
window.addEventListener('resize', handleScroll);
return () => {
el.removeEventListener('scroll', handleScroll);
window.removeEventListener('resize', handleScroll);
};
}, [fade, orientation]);
return (
<ScrollAreaPrimitive.Root
data-slot="scroll-area"
ref={ref}
className={cn('relative overflow-hidden', className)}
{...props}
>
<ScrollAreaPrimitive.Viewport
data-slot="scroll-area-viewport"
ref={viewPortRef}
className="h-full w-full rounded-[inherit]"
>
{fade && (
<>
{orientation === 'vertical' && (
<>
<div
className={cn(
'pointer-events-none absolute top-0 right-0 left-0 h-6 bg-linear-to-b from-white to-transparent transition-opacity duration-200',
fadeState.top ? 'opacity-100' : 'opacity-0',
)}
/>
<div
className={cn(
'pointer-events-none absolute right-0 bottom-0 left-0 h-6 bg-linear-to-t from-white to-transparent transition-opacity duration-200',
fadeState.bottom ? 'opacity-100' : 'opacity-0',
)}
/>
</>
)}
{orientation === 'horizontal' && (
<>
<div
className={cn(
'pointer-events-none absolute top-0 bottom-0 left-0 w-6 bg-linear-to-r from-white to-transparent transition-opacity duration-200',
fadeState.left ? 'opacity-100' : 'opacity-0',
)}
/>
<div
className={cn(
'pointer-events-none absolute top-0 right-0 bottom-0 w-6 bg-linear-to-l from-white to-transparent transition-opacity duration-200',
fadeState.right ? 'opacity-100' : 'opacity-0',
)}
/>
</>
)}
</>
)}
{children}
</ScrollAreaPrimitive.Viewport>
<ScrollBar orientation={orientation} />
<ScrollAreaPrimitive.Corner />
</ScrollAreaPrimitive.Root>
);
});
ScrollArea.displayName = ScrollAreaPrimitive.Root.displayName;
const ScrollBar = React.forwardRef<
React.ComponentRef<typeof ScrollAreaPrimitive.ScrollAreaScrollbar>,
React.ComponentPropsWithoutRef<typeof ScrollAreaPrimitive.ScrollAreaScrollbar>
>(({ className, orientation = 'vertical', ...props }, ref) => (
<ScrollAreaPrimitive.ScrollAreaScrollbar
ref={ref}
data-slot="scroll-area-scrollbar"
orientation={orientation}
className={cn(
'flex touch-none transition-colors select-none',
orientation === 'vertical' && 'h-full w-2.5 border-l border-l-transparent p-px',
orientation === 'horizontal' && 'h-2.5 flex-col border-t border-t-transparent p-px',
className,
)}
{...props}
>
<ScrollAreaPrimitive.ScrollAreaThumb className="relative flex-1 rounded-full bg-border" />
</ScrollAreaPrimitive.ScrollAreaScrollbar>
));
ScrollBar.displayName = ScrollAreaPrimitive.ScrollAreaScrollbar.displayName;
export { ScrollArea, ScrollBar };
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment