Scroll Area
Disclosure & LayoutLocal guide ↗
import { ScrollArea } from "bits-ui" Visibility types
Hover, scroll, auto and always use the same viewport with different scrollbar lifecycles
hover
Build queued1:00
Dependencies restored2:01
Routes compiled3:02
Assets optimized4:03
Health check passed5:04
scroll
Build queued1:00
Dependencies restored2:01
Routes compiled3:02
Assets optimized4:03
Health check passed5:04
auto
Build queued1:00
Dependencies restored2:01
Routes compiled3:02
Assets optimized4:03
Health check passed5:04
always
Build queued1:00
Dependencies restored2:01
Routes compiled3:02
Assets optimized4:03
Health check passed5:04
<script lang="ts">
import { ScrollArea } from 'bits-ui';
const types = ['hover', 'scroll', 'auto', 'always'] as const;
const events = ['Build queued', 'Dependencies restored', 'Routes compiled', 'Assets optimized', 'Health check passed'];
</script>
<div class="grid gap-3 sm:grid-cols-2">
{#each types as type (type)}
<ScrollArea.Root {type} scrollHideDelay={300} class="relative overflow-hidden rounded-xl border border-ink-800 bg-ink-900">
<div class="border-b border-ink-800 px-3 py-2"><span class="chip font-mono">{type}</span></div>
<ScrollArea.Viewport class="h-32 w-full">
<div class="grid gap-2 p-3 pr-5">
{#each events as event, index (event)}
<div class="flex items-center gap-2 text-xs text-ink-200">
<span class="size-1.5 rounded-full bg-brand-500"></span>
<span>{event}</span><span class="ml-auto font-mono text-ink-600">{index + 1}:0{index}</span>
</div>
{/each}
</div>
</ScrollArea.Viewport>
<ScrollArea.Scrollbar orientation="vertical" class="flex w-2.5 touch-none select-none bg-ink-850 p-0.5 data-[state=hidden]:animate-fade-out data-[state=visible]:animate-fade-in">
<ScrollArea.Thumb class="flex-1 rounded-full bg-ink-600" />
</ScrollArea.Scrollbar>
</ScrollArea.Root>
{/each}
</div>
Horizontal collection
A persistent horizontal scrollbar for a wide row of fixed-size cards
Research
Owner · Nora
Prototype
Owner · Ishan
Review
Owner · Mara
Release
Owner · Theo
<script lang="ts">
import { ScrollArea } from 'bits-ui';
const stages = [
{ name: 'Research', owner: 'Nora', tone: 'from-cyan-500/40 to-blue-600/20' },
{ name: 'Prototype', owner: 'Ishan', tone: 'from-violet-500/40 to-fuchsia-600/20' },
{ name: 'Review', owner: 'Mara', tone: 'from-amber-500/40 to-orange-600/20' },
{ name: 'Release', owner: 'Theo', tone: 'from-emerald-500/40 to-teal-600/20' }
];
</script>
<ScrollArea.Root type="always" class="relative mx-auto w-full max-w-xl overflow-hidden rounded-xl border border-ink-800 bg-ink-900">
<ScrollArea.Viewport class="w-full pb-4">
<div class="flex w-max gap-3 p-4">
{#each stages as stage, index (stage.name)}
<article class="w-44 shrink-0 rounded-xl border border-ink-700 bg-gradient-to-br {stage.tone} p-4">
<span class="font-mono text-xs text-ink-400">0{index + 1}</span>
<h3 class="mt-8 font-semibold text-ink-50">{stage.name}</h3>
<p class="muted mt-1 text-xs">Owner · {stage.owner}</p>
</article>
{/each}
</div>
</ScrollArea.Viewport>
<ScrollArea.Scrollbar orientation="horizontal" class="flex h-2.5 touch-none select-none bg-ink-850 p-0.5">
<ScrollArea.Thumb class="rounded-full bg-brand-500" />
</ScrollArea.Scrollbar>
</ScrollArea.Root>
type defaults to hover. In scroll mode bars appear during scrolling; auto keeps them visible only when content overflows, while always shows them even without overflow.
Each Scrollbar requires an orientation and its own Thumb. Add Corner where horizontal and vertical bars meet. The scrollHideDelay default is 600 ms and applies to hover and scroll modes.