Scroll Area
A shadcn-style scroll area component built with Ark UI primitives.
import {
ScrollAreaContent,
ScrollAreaCorner,
ScrollAreaRoot,
ScrollAreaScrollbar,
ScrollAreaThumb,
ScrollAreaViewport,
} from "@/components/ui/scroll-area";
const SCROLL_AREA_LOREM_LONG =
"Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur.";
const ScrollAreaBasicDemo = () => (
<ScrollAreaRoot className="box-border h-34 w-full max-w-96">
<ScrollAreaViewport>
<ScrollAreaContent className="flex flex-col gap-4 py-3 ps-4 pe-6">
<p className="m-0 text-foreground text-sm leading-snug">
{SCROLL_AREA_LOREM_LONG}
</p>
</ScrollAreaContent>
</ScrollAreaViewport>
<ScrollAreaScrollbar orientation="vertical">
<ScrollAreaThumb />
</ScrollAreaScrollbar>
<ScrollAreaCorner />
</ScrollAreaRoot>
);
export default ScrollAreaBasicDemo;
Installation
npx shadcn@latest add @ark-cn/scroll-areaInstall the dependency required by this primitive:
npm install @ark-ui/reactCopy the component source into your app:
TSXcomponents/ui/scroll-area.tsx
"use client";
import {
ScrollArea as ScrollAreaPrimitive,
useScrollArea,
useScrollAreaContext,
} from "@ark-ui/react/scroll-area";
import {
type ComponentProps,
createContext,
type ReactNode,
useContext,
} from "react";
import { cn } from "@/lib/utils";
export { useScrollArea, useScrollAreaContext };
export type ScrollAreaScrollbarVisibility = "hover" | "always";
const ScrollAreaScrollbarVisibilityContext =
createContext<ScrollAreaScrollbarVisibility>("hover");
const viewportClassName = cn(
"z-0 col-start-1 row-start-1 box-border block size-full min-h-0 min-w-0 rounded-[inherit] outline-none ring-offset-background",
"overscroll-contain [scrollbar-width:none] [&::-webkit-scrollbar]:hidden",
"focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2",
);
const scrollbarBaseClassName = cn(
"z-10 flex touch-none select-none rounded-full bg-muted/60 transition-opacity duration-150 ease-out",
"before:absolute before:content-['']",
"data-[orientation=vertical]:mx-1 data-[orientation=vertical]:my-2 data-[orientation=vertical]:w-1.5 data-[orientation=vertical]:flex-col",
"data-[orientation=vertical]:before:left-1/2 data-[orientation=vertical]:before:h-full data-[orientation=vertical]:before:w-5 data-[orientation=vertical]:before:-translate-x-1/2",
"data-[orientation=horizontal]:mx-2 data-[orientation=horizontal]:my-1 data-[orientation=horizontal]:h-1.5 data-[orientation=horizontal]:flex-row",
"data-[orientation=horizontal]:before:h-5 data-[orientation=horizontal]:before:w-full",
"data-[orientation=vertical]:[&:not([data-overflow-y])]:hidden",
"data-[orientation=horizontal]:[&:not([data-overflow-x])]:hidden",
);
const scrollbarVisibilityClassName: Record<
ScrollAreaScrollbarVisibility,
string
> = {
hover: cn(
"opacity-0 pointer-events-none",
"group-hover/scroll-area:pointer-events-auto group-hover/scroll-area:opacity-100",
"group-focus-within/scroll-area:pointer-events-auto group-focus-within/scroll-area:opacity-100",
"data-hover:pointer-events-auto data-hover:opacity-100",
"data-scrolling:pointer-events-auto data-scrolling:opacity-100 data-scrolling:duration-0",
"data-dragging:pointer-events-auto data-dragging:opacity-100 data-dragging:duration-0",
),
always: cn("pointer-events-auto opacity-100"),
};
/* No flex-1: Zag sets height/width from --thumb-* and translate3d on the thumb; flex-1 fills the track and breaks offsetHeight + drag. */
const thumbClassName = cn(
"relative flex-none rounded-full bg-foreground/25 transition-colors",
"pointer-events-auto w-full max-w-full",
"data-[orientation=horizontal]:h-full data-[orientation=horizontal]:max-h-full",
"data-hover:bg-foreground/40 data-dragging:bg-foreground/55",
);
export type ScrollAreaRootProps = ComponentProps<
typeof ScrollAreaPrimitive.Root
> & {
scrollbarVisibility?: ScrollAreaScrollbarVisibility;
};
export const ScrollAreaRoot = ({
className,
scrollbarVisibility = "hover",
...props
}: ScrollAreaRootProps) => (
<ScrollAreaScrollbarVisibilityContext.Provider value={scrollbarVisibility}>
<ScrollAreaPrimitive.Root
className={cn(
"group/scroll-area relative isolate grid min-h-0 min-w-0 grid-cols-[minmax(0,1fr)] grid-rows-[minmax(0,1fr)] overflow-hidden rounded-lg border border-border bg-card text-card-foreground",
className,
)}
data-slot="scroll-area-root"
{...props}
/>
</ScrollAreaScrollbarVisibilityContext.Provider>
);
export type ScrollAreaViewportProps = ComponentProps<
typeof ScrollAreaPrimitive.Viewport
>;
export const ScrollAreaViewport = ({
className,
...props
}: ScrollAreaViewportProps) => (
<ScrollAreaPrimitive.Viewport
className={cn(viewportClassName, className)}
data-slot="scroll-area-viewport"
{...props}
/>
);
export type ScrollAreaContentProps = ComponentProps<
typeof ScrollAreaPrimitive.Content
>;
export const ScrollAreaContent = ({
className,
...props
}: ScrollAreaContentProps) => (
<ScrollAreaPrimitive.Content
className={cn(className)}
data-slot="scroll-area-content"
{...props}
/>
);
export type ScrollAreaScrollbarProps = ComponentProps<
typeof ScrollAreaPrimitive.Scrollbar
>;
export const ScrollAreaScrollbar = ({
className,
...props
}: ScrollAreaScrollbarProps) => {
const visibility = useContext(ScrollAreaScrollbarVisibilityContext);
return (
<ScrollAreaPrimitive.Scrollbar
className={cn(
scrollbarBaseClassName,
scrollbarVisibilityClassName[visibility],
className,
)}
data-slot="scroll-area-scrollbar"
{...props}
/>
);
};
export type ScrollAreaThumbProps = ComponentProps<
typeof ScrollAreaPrimitive.Thumb
>;
export const ScrollAreaThumb = ({
className,
...props
}: ScrollAreaThumbProps) => (
<ScrollAreaPrimitive.Thumb
className={cn(thumbClassName, className)}
data-slot="scroll-area-thumb"
{...props}
/>
);
export type ScrollAreaCornerProps = ComponentProps<
typeof ScrollAreaPrimitive.Corner
>;
export const ScrollAreaCorner = ({
className,
...props
}: ScrollAreaCornerProps) => (
<ScrollAreaPrimitive.Corner
className={cn("z-20 bg-transparent", className)}
data-slot="scroll-area-corner"
{...props}
/>
);
export type ScrollAreaRootProviderProps = ComponentProps<
typeof ScrollAreaPrimitive.RootProvider
> & {
scrollbarVisibility?: ScrollAreaScrollbarVisibility;
};
export const ScrollAreaRootProvider = ({
className,
scrollbarVisibility = "hover",
...props
}: ScrollAreaRootProviderProps) => (
<ScrollAreaScrollbarVisibilityContext.Provider value={scrollbarVisibility}>
<ScrollAreaPrimitive.RootProvider
className={cn(
"group/scroll-area relative isolate grid min-h-0 min-w-0 grid-cols-[minmax(0,1fr)] grid-rows-[minmax(0,1fr)] overflow-hidden rounded-lg border border-border bg-card text-card-foreground",
className,
)}
data-slot="scroll-area-root-provider"
{...props}
/>
</ScrollAreaScrollbarVisibilityContext.Provider>
);
export type ScrollAreaContextProps = ComponentProps<
typeof ScrollAreaPrimitive.Context
>;
export const ScrollAreaContext = (props: ScrollAreaContextProps) => (
<ScrollAreaPrimitive.Context {...props} />
);
export type ScrollAreaProps = ScrollAreaRootProps & {
scrollFade?: boolean;
scrollbarGutter?: boolean;
children?: ReactNode;
};
export const ScrollArea = ({
className,
children,
scrollFade,
scrollbarGutter,
...props
}: ScrollAreaProps) => (
<ScrollAreaRoot className={className} {...props}>
<ScrollAreaViewport
className={cn(
scrollbarGutter && "pe-2.5",
scrollFade &&
"mask-[linear-gradient(to_bottom,transparent,black_12px,black_calc(100%-12px),transparent)]",
)}
>
<ScrollAreaContent>{children}</ScrollAreaContent>
</ScrollAreaViewport>
<ScrollAreaScrollbar orientation="vertical">
<ScrollAreaThumb />
</ScrollAreaScrollbar>
<ScrollAreaCorner />
</ScrollAreaRoot>
);
Add the following CSS to your stylesheet (e.g. styles.css):
@layer base {
[data-scope="scroll-area"][data-part="viewport"] {
scrollbar-width: none;
}
[data-scope="scroll-area"][data-part="viewport"]::-webkit-scrollbar {
display: none;
}
}Update import aliases to match your project setup.
Usage
import * as ScrollArea from "@/components/ui/scroll-area"Read exported parts in src/components/ui/scroll-area.tsx and compose the primitive according to the Ark UI pattern for this component.
Examples
Basic
import {
ScrollAreaContent,
ScrollAreaCorner,
ScrollAreaRoot,
ScrollAreaScrollbar,
ScrollAreaThumb,
ScrollAreaViewport,
} from "@/components/ui/scroll-area";
const SCROLL_AREA_LOREM_LONG =
"Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur.";
const ScrollAreaBasicDemo = () => (
<ScrollAreaRoot className="box-border h-34 w-full max-w-96">
<ScrollAreaViewport>
<ScrollAreaContent className="flex flex-col gap-4 py-3 ps-4 pe-6">
<p className="m-0 text-foreground text-sm leading-snug">
{SCROLL_AREA_LOREM_LONG}
</p>
</ScrollAreaContent>
</ScrollAreaViewport>
<ScrollAreaScrollbar orientation="vertical">
<ScrollAreaThumb />
</ScrollAreaScrollbar>
<ScrollAreaCorner />
</ScrollAreaRoot>
);
export default ScrollAreaBasicDemo;
Horizontal
import {
ScrollAreaContent,
ScrollAreaCorner,
ScrollAreaRoot,
ScrollAreaScrollbar,
ScrollAreaThumb,
ScrollAreaViewport,
} from "@/components/ui/scroll-area";
const SCROLL_AREA_LOREM_MEDIUM =
"Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.";
const ScrollAreaHorizontalDemo = () => (
<ScrollAreaRoot className="box-border h-auto w-full max-w-96">
<ScrollAreaViewport>
<ScrollAreaContent className="flex flex-col gap-4 py-3 ps-4 pe-6">
<p className="m-0 w-[50vw] max-w-none text-foreground text-sm leading-snug">
{SCROLL_AREA_LOREM_MEDIUM}
</p>
</ScrollAreaContent>
</ScrollAreaViewport>
<ScrollAreaScrollbar orientation="horizontal">
<ScrollAreaThumb />
</ScrollAreaScrollbar>
<ScrollAreaCorner />
</ScrollAreaRoot>
);
export default ScrollAreaHorizontalDemo;
Both directions
import {
ScrollAreaContent,
ScrollAreaCorner,
ScrollAreaRoot,
ScrollAreaScrollbar,
ScrollAreaThumb,
ScrollAreaViewport,
} from "@/components/ui/scroll-area";
const SCROLL_AREA_LOREM_MEDIUM =
"Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.";
const SCROLL_AREA_LOREM_PARA2 =
"Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque laudantium, totam rem aperiam.";
const SCROLL_AREA_LOREM_PARA3 =
"At vero eos et accusamus et iusto odio dignissimos ducimus qui blanditiis praesentium voluptatum deleniti atque.";
const ScrollAreaBothDirectionsDemo = () => (
<ScrollAreaRoot className="box-border h-48 w-96 max-w-[calc(100vw-8rem)]">
<ScrollAreaViewport>
<ScrollAreaContent className="flex flex-col gap-4 py-3 ps-4 pe-6">
<p className="m-0 w-[50vw] max-w-none text-foreground text-sm leading-snug">
{SCROLL_AREA_LOREM_MEDIUM}
</p>
<p className="m-0 w-[50vw] max-w-none text-foreground text-sm leading-snug">
{SCROLL_AREA_LOREM_PARA2}
</p>
<p className="m-0 w-[50vw] max-w-none text-foreground text-sm leading-snug">
{SCROLL_AREA_LOREM_PARA3}
</p>
<p className="m-0 w-[50vw] max-w-none text-foreground text-sm leading-snug">
{SCROLL_AREA_LOREM_MEDIUM}
</p>
<p className="m-0 w-[50vw] max-w-none text-foreground text-sm leading-snug">
{SCROLL_AREA_LOREM_PARA2}
</p>
<p className="m-0 w-[50vw] max-w-none text-foreground text-sm leading-snug">
{SCROLL_AREA_LOREM_PARA3}
</p>
<p className="m-0 w-[50vw] max-w-none text-foreground text-sm leading-snug">
{SCROLL_AREA_LOREM_MEDIUM}
</p>
<p className="m-0 w-[50vw] max-w-none text-foreground text-sm leading-snug">
{SCROLL_AREA_LOREM_PARA2}
</p>
<p className="m-0 w-[50vw] max-w-none text-foreground text-sm leading-snug">
{SCROLL_AREA_LOREM_PARA3}
</p>
<p className="m-0 w-[50vw] max-w-none text-foreground text-sm leading-snug">
{SCROLL_AREA_LOREM_MEDIUM}
</p>
<p className="m-0 w-[50vw] max-w-none text-foreground text-sm leading-snug">
{SCROLL_AREA_LOREM_PARA2}
</p>
<p className="m-0 w-[50vw] max-w-none text-foreground text-sm leading-snug">
{SCROLL_AREA_LOREM_PARA3}
</p>
<p className="m-0 w-[50vw] max-w-none text-foreground text-sm leading-snug">
{SCROLL_AREA_LOREM_MEDIUM}
</p>
<p className="m-0 w-[50vw] max-w-none text-foreground text-sm leading-snug">
{SCROLL_AREA_LOREM_PARA2}
</p>
<p className="m-0 w-[50vw] max-w-none text-foreground text-sm leading-snug">
{SCROLL_AREA_LOREM_PARA3}
</p>
</ScrollAreaContent>
</ScrollAreaViewport>
<ScrollAreaScrollbar orientation="vertical">
<ScrollAreaThumb />
</ScrollAreaScrollbar>
<ScrollAreaScrollbar orientation="horizontal">
<ScrollAreaThumb />
</ScrollAreaScrollbar>
<ScrollAreaCorner />
</ScrollAreaRoot>
);
export default ScrollAreaBothDirectionsDemo;
Nested
import {
ScrollAreaContent,
ScrollAreaCorner,
ScrollAreaRoot,
ScrollAreaScrollbar,
ScrollAreaThumb,
ScrollAreaViewport,
} from "@/components/ui/scroll-area";
const SCROLL_AREA_LOREM_NEST_OUTER =
"Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.";
const SCROLL_AREA_LOREM_NEST_INNER =
"This is a nested scroll area. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur.";
const ScrollAreaNestedDemo = () => (
<ScrollAreaRoot className="box-border h-48 w-96 max-w-[calc(100vw-8rem)]">
<ScrollAreaViewport>
<ScrollAreaContent className="flex flex-col gap-4 py-3 ps-4 pe-6">
<p className="m-0 text-foreground text-sm leading-snug">
{SCROLL_AREA_LOREM_NEST_OUTER}
</p>
<ScrollAreaRoot className="box-border h-32 w-full">
<ScrollAreaViewport>
<ScrollAreaContent className="flex flex-col gap-4 py-3 ps-4 pe-6">
<p className="m-0 text-foreground text-sm leading-snug">
{SCROLL_AREA_LOREM_NEST_INNER}
</p>
<p className="m-0 text-foreground text-sm leading-snug">
{SCROLL_AREA_LOREM_NEST_INNER}
</p>
<p className="m-0 text-foreground text-sm leading-snug">
{SCROLL_AREA_LOREM_NEST_INNER}
</p>
<p className="m-0 text-foreground text-sm leading-snug">
{SCROLL_AREA_LOREM_NEST_INNER}
</p>
</ScrollAreaContent>
</ScrollAreaViewport>
<ScrollAreaScrollbar orientation="vertical">
<ScrollAreaThumb />
</ScrollAreaScrollbar>
<ScrollAreaCorner />
</ScrollAreaRoot>
</ScrollAreaContent>
</ScrollAreaViewport>
<ScrollAreaScrollbar orientation="vertical">
<ScrollAreaThumb />
</ScrollAreaScrollbar>
<ScrollAreaCorner />
</ScrollAreaRoot>
);
export default ScrollAreaNestedDemo;
Root provider
import { Button } from "@/components/ui/button";
import {
ScrollAreaContent,
ScrollAreaCorner,
ScrollAreaRootProvider,
ScrollAreaScrollbar,
ScrollAreaThumb,
ScrollAreaViewport,
useScrollArea,
} from "@/components/ui/scroll-area";
const SCROLL_AREA_LOREM_LONG =
"Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.";
const ScrollAreaRootProviderDemo = () => {
const scrollArea = useScrollArea();
return (
<div className="flex max-w-md flex-col gap-3">
<div className="flex flex-wrap gap-2">
<Button
onClick={() => scrollArea.scrollToEdge({ edge: "top" })}
size="sm"
type="button"
variant="outline"
>
Scroll to top
</Button>
<Button
onClick={() => scrollArea.scrollToEdge({ edge: "bottom" })}
size="sm"
type="button"
variant="outline"
>
Scroll to bottom
</Button>
</div>
<ScrollAreaRootProvider
className="box-border h-34 w-full"
value={scrollArea}
>
<ScrollAreaViewport>
<ScrollAreaContent className="flex flex-col gap-4 py-3 ps-4 pe-6">
<p className="m-0 text-foreground text-sm leading-snug">
{SCROLL_AREA_LOREM_LONG}
</p>
<p className="m-0 text-foreground text-sm leading-snug">
{SCROLL_AREA_LOREM_LONG}
</p>
<p className="m-0 text-foreground text-sm leading-snug">
{SCROLL_AREA_LOREM_LONG}
</p>
<p className="m-0 text-foreground text-sm leading-snug">
{SCROLL_AREA_LOREM_LONG}
</p>
<p className="m-0 text-foreground text-sm leading-snug">
{SCROLL_AREA_LOREM_LONG}
</p>
<p className="m-0 text-foreground text-sm leading-snug">
{SCROLL_AREA_LOREM_LONG}
</p>
<p className="m-0 text-foreground text-sm leading-snug">
{SCROLL_AREA_LOREM_LONG}
</p>
</ScrollAreaContent>
</ScrollAreaViewport>
<ScrollAreaScrollbar orientation="vertical">
<ScrollAreaThumb />
</ScrollAreaScrollbar>
<ScrollAreaCorner />
</ScrollAreaRootProvider>
</div>
);
};
export default ScrollAreaRootProviderDemo;
Tags
import { ScrollArea } from "@/components/ui/scroll-area";
const tags = Array.from({ length: 50 }, (_, i) => `v1.0.0-alpha.${i}`);
const ScrollAreaTagsDemo = () => (
<ScrollArea className="h-64 max-w-sm">
<div className="px-4 py-2">
<h4 className="mb-2 font-medium text-foreground text-sm">Tags</h4>
<div className="flex flex-col gap-1">
{tags.map((tag) => (
<div className="text-sm" key={tag}>
{tag}
</div>
))}
</div>
</div>
</ScrollArea>
);
export default ScrollAreaTagsDemo;
Scroll fade
import { ScrollArea } from "@/components/ui/scroll-area";
const tags = Array.from({ length: 50 }, (_, i) => `v1.0.0-alpha.${i}`);
const ScrollAreaScrollFadeDemo = () => (
<ScrollArea className="h-64 max-w-sm" scrollFade>
<div className="px-4 py-2">
<h4 className="mb-2 font-medium text-foreground text-sm">Tags</h4>
<div className="flex flex-col gap-1">
{tags.map((tag) => (
<div className="text-sm" key={tag}>
{tag}
</div>
))}
</div>
</div>
</ScrollArea>
);
export default ScrollAreaScrollFadeDemo;
Horizontal items
import {
ScrollAreaContent,
ScrollAreaCorner,
ScrollAreaRoot,
ScrollAreaScrollbar,
ScrollAreaThumb,
ScrollAreaViewport,
} from "@/components/ui/scroll-area";
const ScrollAreaHorizontalItemsDemo = () => (
<ScrollAreaRoot className="max-w-md">
<ScrollAreaViewport className="pb-2.5">
<ScrollAreaContent className="flex w-max gap-4 p-4">
{Array.from({ length: 20 }).map((_, i) => (
<div
className="flex h-20 w-32 shrink-0 items-center justify-center rounded-md bg-muted"
key={String(i)}
>
<span className="font-medium text-sm">Item {i + 1}</span>
</div>
))}
</ScrollAreaContent>
</ScrollAreaViewport>
<ScrollAreaScrollbar orientation="horizontal">
<ScrollAreaThumb />
</ScrollAreaScrollbar>
<ScrollAreaCorner />
</ScrollAreaRoot>
);
export default ScrollAreaHorizontalItemsDemo;
Scrollbar gutter
import { ScrollArea } from "@/components/ui/scroll-area";
const rows = Array.from({ length: 40 }, (_, i) => `Reserved gutter · ${i}`);
const ScrollAreaScrollbarGutterDemo = () => (
<ScrollArea className="h-52 max-w-xs" scrollbarGutter>
<div className="flex flex-col gap-1 px-4 py-2">
{rows.map((row) => (
<div className="text-sm" key={row}>
{row}
</div>
))}
</div>
</ScrollArea>
);
export default ScrollAreaScrollbarGutterDemo;
Scroll context
At top: true · At bottom: false
import {
ScrollAreaContent,
ScrollAreaContext,
ScrollAreaCorner,
ScrollAreaRoot,
ScrollAreaScrollbar,
ScrollAreaThumb,
ScrollAreaViewport,
} from "@/components/ui/scroll-area";
const SCROLL_AREA_LOREM_LONG =
"Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.";
const ScrollAreaContextDemo = () => (
<ScrollAreaRoot className="box-border h-34 w-full max-w-sm">
<ScrollAreaContext>
{({ isAtTop, isAtBottom }) => (
<>
<ScrollAreaViewport>
<ScrollAreaContent className="flex flex-col gap-4 py-3 ps-4 pe-6">
<p className="m-0 text-foreground text-sm leading-snug">
{SCROLL_AREA_LOREM_LONG}
</p>
</ScrollAreaContent>
</ScrollAreaViewport>
<ScrollAreaScrollbar orientation="vertical">
<ScrollAreaThumb />
</ScrollAreaScrollbar>
<ScrollAreaCorner />
<p className="mt-2 text-muted-foreground text-xs">
At top:{" "}
<span className="font-medium text-foreground">
{String(isAtTop)}
</span>
{" · "}At bottom:{" "}
<span className="font-medium text-foreground">
{String(isAtBottom)}
</span>
</p>
</>
)}
</ScrollAreaContext>
</ScrollAreaRoot>
);
export default ScrollAreaContextDemo;
API reference
This component mirrors the upstream Ark UI primitive. All props and DOM behavior are defined by Ark unless you see an ark-cn-only row below.
ScrollArea
| Prop | Type | Description |
|---|---|---|
| scrollFade? | boolean | Applies a vertical fade mask on the viewport. |
| scrollbarGutter? | boolean | Reserves inline end space for scrollbar stability. |
ScrollAreaRoot
| Prop | Type | Description |
|---|---|---|
| scrollbarVisibility? | "hover" | "always" | Controls scrollbar opacity/pointer behavior. |
See the ARK UI documentation for the full API.