@lattice-ui/scroll-area Part of the stable direction toward v1.0.
What It Is For
Use ScrollArea when you want custom scrollbars and thumbs around a Roblox ScrollingFrame viewport.
It is useful when native scroll visuals do not match the product language but native scrolling behavior should remain intact.
import { ScrollArea } from "@lattice-ui/scroll-area";
export function ScrollAreaExample() {
return (
<frame BackgroundTransparency={1} Size={UDim2.fromOffset(560, 260)}>
<ScrollArea.Root>
<frame BackgroundTransparency={1} Size={UDim2.fromOffset(420, 200)}>
<ScrollArea.Viewport asChild>
<scrollingframe
AutomaticCanvasSize={Enum.AutomaticSize.None}
BackgroundColor3={Color3.fromRGB(34, 41, 54)}
BorderSizePixel={0}
CanvasSize={UDim2.fromOffset(760, 420)}
ScrollBarImageTransparency={1}
ScrollBarThickness={0}
ScrollingDirection={Enum.ScrollingDirection.XY}
Size={UDim2.fromOffset(380, 160)}
>
<frame
BackgroundColor3={Color3.fromRGB(53, 104, 196)}
BorderSizePixel={0}
Position={UDim2.fromOffset(24, 24)}
Size={UDim2.fromOffset(220, 90)}
/>
<frame
BackgroundColor3={Color3.fromRGB(173, 80, 80)}
BorderSizePixel={0}
Position={UDim2.fromOffset(520, 260)}
Size={UDim2.fromOffset(180, 100)}
/>
</scrollingframe>
</ScrollArea.Viewport>
<ScrollArea.Scrollbar asChild orientation="vertical">
<frame
BackgroundColor3={Color3.fromRGB(60, 76, 104)}
BorderSizePixel={0}
Position={UDim2.fromOffset(384, 0)}
Size={UDim2.fromOffset(8, 160)}
>
<ScrollArea.Thumb asChild orientation="vertical">
<frame
BackgroundColor3={Color3.fromRGB(172, 181, 196)}
BorderSizePixel={0}
Size={UDim2.fromScale(1, 1)}
>
<uicorner CornerRadius={new UDim(1, 0)} />
</frame>
</ScrollArea.Thumb>
</frame>
</ScrollArea.Scrollbar>
<ScrollArea.Scrollbar asChild orientation="horizontal">
<frame
BackgroundColor3={Color3.fromRGB(60, 76, 104)}
BorderSizePixel={0}
Position={UDim2.fromOffset(0, 164)}
Size={UDim2.fromOffset(380, 8)}
>
<ScrollArea.Thumb asChild orientation="horizontal">
<frame
BackgroundColor3={Color3.fromRGB(172, 181, 196)}
BorderSizePixel={0}
Size={UDim2.fromScale(1, 1)}
>
<uicorner CornerRadius={new UDim(1, 0)} />
</frame>
</ScrollArea.Thumb>
</frame>
</ScrollArea.Scrollbar>
<ScrollArea.Corner asChild>
<frame
BackgroundColor3={Color3.fromRGB(60, 76, 104)}
BorderSizePixel={0}
Position={UDim2.fromOffset(384, 164)}
Size={UDim2.fromOffset(8, 8)}
/>
</ScrollArea.Corner>
</frame>
</ScrollArea.Root>
</frame>
);
} Live demo is experimental and may contain bugs.
Install
Global CLI command: lattice add scroll-area
Monorepo local script
Use your package manager wrapper when running the local lattice command.
pnpm lattice add scroll-areaPublic Exports
-
ScrollArea -
ScrollArea.Root -
ScrollArea.Viewport -
ScrollArea.Scrollbar -
ScrollArea.Thumb -
ScrollArea.Corner -
resolveCanvasPositionFromThumbOffset -
resolveCanvasPositionFromTrackPosition -
resolveThumbOffset -
resolveThumbOffsetFromPointerDelta -
resolveThumbOffsetFromTrackPosition -
resolveThumbSize
State Model
ScrollArea.Roottracks viewport metrics for both axes, scrollbar visibility policy, and recent scroll activity.typedetermines whether scrollbars are always visible, only visible while scrolling, or auto-shown based on overflow.- Scrollbar and thumb primitives read the shared metrics; they do not maintain independent scroll position state.
Key API
ScrollArea.Root
Use type and scrollHideDelayMs to define how visible and reactive the custom scrollbars should feel.
ScrollArea.Viewport
Bind this to the actual ScrollingFrame host so the package can observe canvas size and scroll position.
ScrollArea.Scrollbar / Thumb
Set orientation to wire each scrollbar and thumb to the correct axis.
Composition Patterns
Custom content panes
Wrap a ScrollingFrame viewport with vertical and horizontal scrollbar primitives when the app needs a branded scroll treatment.
Large settings or logs panels
Use the auto visibility policy for dense content areas where permanent scrollbars would add noise.
Cautions / Limits
- Thumb movement reflects shared viewport metrics, so the viewport host must be the authoritative scroll owner.
- This package customizes the chrome around scrolling; it does not replace Roblox scrolling behavior or content virtualization.