Knit UI
GitHub

Playground

Loading playground…

false
Playground
<ScrollArea
  scrollbars="xy"
  type="hover"
  scrollbarSize={12}
  offsetScrollbars={false}
  scrollHideDelay={1000}
  width={320}
  height={200}
/>

Examples

Vertical Only

Vertical-only scrolling — horizontal overflow is hidden.

Loading example…

Vertical OnlySourceStorybook

Horizontal Only

Horizontal-only scrolling — a wide single-line row of items.

Loading example…

Horizontal OnlySourceStorybook

Both Axes

Both axes active — a wide, tall content area that scrolls in every direction.

Loading example…

Both AxesSourceStorybook

Shadows

Elevation shadow ladder applied to the ScrollArea frame via the inherited shadow prop. Distinct from EdgeShadows, which is the scroll-fade overlay.

Loading example…

ShadowsSourceStorybook

Scrollbar Sizes

Thick scrollbar (16 px) vs. thin scrollbar (4 px) side by side.

Loading example…

Scrollbar SizesSourceStorybook

Scroll Position Tracking

onScrollPositionChange — the current x/y offset is displayed in real time.

Loading example…

Scroll Position TrackingSourceStorybook

Autosize

ScrollArea.Autosize grows with its content until maxHeight is reached, then scrolls.

Loading example…

AutosizeSourceStorybook

Custom Scrollbars

Fully custom scrollbars — the rail, thumb and corner are styled via scrollbarProps, thumbProps and cornerProps. These accept any Box prop, so colour, radius, padding and even hover/press styles are yours to set.

Loading example…

Custom ScrollbarsSourceStorybook

Visibility Types

type behaviours side by side — always (persistent), hover (on hover/scroll), scroll (while scrolling only) and never (no scrollbars, still scrolls).

Loading example…

Visibility TypesSourceStorybook

Imperative Controls

Imperative handle — the forwarded ref is a ScrollAreaHandle exposing scrollTo, scrollToTop, scrollToBottom, scrollToEnd, scrollIntoView, getViewport and getScrollPosition.

Loading example…

Imperative ControlsSourceStorybook

Edge Shadows

Edge fade shadows — overlays fade in on each scrollable edge to hint at hidden content, and fade out as you reach that edge. Driven by the shadows prop.

Loading example…

Edge ShadowsSourceStorybook

Infinite Scroll

Infinite scroll — onReachBottom (with a reachThreshold) appends more items as you approach the end. onReachTop/onReachStart/onReachEnd/onScrollEnd round out the edge callbacks.

Loading example…

Infinite ScrollSourceStorybook

Stick To Bottom

Stick-to-bottom — a chat/log pattern. New lines pin to the bottom while you're already there; scroll up and the follow releases until you return.

Loading example…

Stick To BottomSourceStorybook

Track Paging

Track click paging — with trackClickBehavior="page", clicking the rail outside the thumb steps one viewport toward the click instead of jumping.

Loading example…

Track PagingSourceStorybook

Hover Grow

Hover-grow thumb — the rail rests thin (idleScrollbarSize) and animates to scrollbarSize on hover/drag, macOS-style. Hover the area to see it thicken.

Loading example…

Hover GrowSourceStorybook

Keyboard

Keyboard scrolling — focus the area (Tab) and use Arrow / PageUp / PageDown / Home / End / Space to scroll. Enabled by default via keyboardScrolling.

Loading example…

KeyboardSourceStorybook

Styles

Per-slot styles targets individual parts — here the viewport, scrollbar, thumb, and corner.

Loading example…

StylesSourceStorybook

Props

PropTypeDefaultDescription
cornerPropsPartial<GetFinalProps<RNTamaguiViewNonStyleProps, StackStyleBase, { shadow?: "xs" | "sm" | "md" | "lg" | "xl" | "xxs" | "xxl" | undefined; }>> | undefinedProps spread onto the corner shown where the two scrollbars meet.
disabled systemboolean | undefined
handleRefRef<ScrollAreaHandle> | undefinedImperative handle for programmatic scrolling. An alternative to the forwarded `ref` (which is also the {@link ScrollAreaHandle}); useful when the `ref` is needed elsewhere.
idleScrollbarSizenumber | undefined6Idle scrollbar thickness (px). The rail rests this thin and animates up to `scrollbarSize` on hover/drag (macOS-style overlay). Set equal to `scrollbarSize` to disable the grow.
keyboardScrollingboolean | undefinedtrueMake the viewport focusable and scroll it with Arrow / Page / Home / End / Space keys. Web only — a no-op on native.
keyboardShouldPersistTapsboolean | "always" | "never" | "handled" | undefinedRN default (`"never"`)How taps are delivered while the on-screen keyboard is up (native only — maps to the React Native `ScrollView` prop of the same name; ignored on web). The RN default (`"never"`) swallows the FIRST tap to dismiss the keyboard, so a tap on a pressable child (e.g. a dropdown option in a focus-driven combobox) is eaten — and dismissing the keyboard blurs the field, closing the dropdown before the option's press can land. `"handled"` keeps the keyboard up when a child handles the tap, so the option is selected normally while a tap on empty space still dismisses.
keyStepnumber | undefined40Pixels scrolled per arrow-key press.
offsetScrollbarsboolean | "x" | "y" | "present" | undefinedfalseReserve stable gutter space so content does not sit under the overlay scrollbar.
onReachBottom(() => void) | undefinedFired once when the viewport reaches the bottom.
onReachEnd(() => void) | undefinedFired once when the viewport reaches the far right (horizontal end).
onReachStart(() => void) | undefinedFired once when the viewport reaches the far left (horizontal start).
onReachTop(() => void) | undefinedFired once when the viewport reaches the top.
onScrollEnd((position: ScrollPosition) => void) | undefinedFired after scrolling settles, with the resting `{ x, y }` offset.
onScrollPositionChange((position: ScrollPosition) => void) | undefinedCalled with the `{ x, y }` scroll offset whenever the viewport scrolls.
reachThresholdnumber | undefined0Distance from an edge (px) at which the reach callbacks fire.
renderShadow((edge: Edge, size: number) => ReactNode) | undefinedCustom edge-overlay renderer — an escape hatch for true native gradients (e.g. `expo-linear-gradient`). Called per visible edge; return `null` to fall back to the default fade.
scrollbarPropsPartial<GetFinalProps<RNTamaguiViewNonStyleProps, StackStyleBase, { shadow?: "xs" | "sm" | "md" | "lg" | "xl" | "xxs" | "xxl" | undefined; }>> | undefinedProps spread onto each scrollbar track (style the rail).
scrollbarsScrollAreaScrollbars | undefined
x · y · xy · false
"xy"Axes on which scrolling is enabled.
scrollbarSizenumber | undefined12Scrollbar (thumb + rail) thickness in px.
scrollHideDelaynumber | undefined1000Delay in ms before scrollbars hide in `hover` / `scroll` modes.
scrollValueXSharedValue<number> | undefinedHorizontal counterpart of {@link scrollValueY}.
scrollValueYSharedValue<number> | undefinedReanimated shared value the live vertical scroll offset is mirrored INTO on the UI thread every frame, with NO JS-thread round-trip. Pass this when a reanimated animation (e.g. a parallax / collapsing header) needs the live offset: create it with `useSharedValue(0)` and feed the SAME value to your `useAnimatedStyle`. Prefer this over `onScrollPositionChange` for animation — that callback hops to JS via `runOnJS`, so the animation stutters whenever the JS thread is busy. Native only; the web ScrollArea ignores it (drive web from `onScrollPositionChange` + React state instead).
shadow system"xs" | "sm" | "md" | "lg" | "xl" | "xxs" | "xxl" | undefined
shadowColorstring | undefinedFade colour. Web fades from this colour to transparent; native paints a translucent scrim of it. Defaults to the theme background.
shadowsShadowEdges | undefinedfalseShow fade overlays on scrollable edges to hint at hidden content.
shadowSizenumber | undefined32Fade depth in px.
stickToBottomboolean | undefinedfalseAuto-scroll to the bottom as content grows, but only while the user is already near the bottom — released the moment they scroll up.
stickToBottomThresholdnumber | undefined24Slack (px) within which "at the bottom" counts for sticking.
styles systemSlotStyles<ScrollAreaStyles> | undefinedUniform per-slot style passthrough. Sugar over the discrete `*Props` above; each slot layers after its matching `*Props`. See {@link ScrollAreaStyles}.
theme systemThemeName | null | undefinedApplies a theme to this element
thumbPropsPartial<GetFinalProps<RNTamaguiViewNonStyleProps, StackStyleBase, { shadow?: "xs" | "sm" | "md" | "lg" | "xl" | "xxs" | "xxl" | undefined; }>> | undefinedProps spread onto each scrollbar thumb (style the draggable handle).
trackClickBehavior"page" | "jump" | undefined"jump"What a click on the rail (outside the thumb) does: `jump` centres the thumb on the click; `page` steps one viewport toward it.
typeScrollAreaType | undefined
hover · auto · always · scroll · never
"hover"Scrollbar visibility: `hover` (on hover/scroll), `scroll` (while scrolling), `auto` (whenever content overflows), `always`, or `never`.
viewportPropsPartial<GetFinalProps<RNTamaguiViewNonStyleProps, StackStyleBase, { shadow?: "xs" | "sm" | "md" | "lg" | "xl" | "xxs" | "xxl" | undefined; }>> | undefinedProps spread onto the scrollable viewport.
viewportRefRef<TamaguiElement> | undefinedRef assigned to the scrollable viewport element.

Style slots

Every part of ScrollArea can be styled through the styles prop. Explicit props on the component always win over slot styles.

SlotUsage
cornerstyles={{ corner: { … } }}
scrollbarstyles={{ scrollbar: { … } }}
thumbstyles={{ thumb: { … } }}
viewportstyles={{ viewport: { … } }}
Accessibility & DOM props (2)
PropType
keyKey | null | undefined
refRef<ScrollAreaHandle> | undefined

Plus 497 inherited style props from Box — the full Tamagui/React Native style surface, including token shorthands like p, mx, bg and c. See Tokens for the scales they accept, or the full list.