Scroll Area
A scroll area provides a custom-scrollbar viewport for overflow content.
Features
- Programmatic scrolling to edges or coordinates
- Scroll position and overflow state helpers
- Scrollbar state for styling (
hovering,scrolling,hidden) - Horizontal and vertical scrollbar support
Installation
Install the scroll area package:
npm install @zag-js/scroll-area @zag-js/react # or yarn add @zag-js/scroll-area @zag-js/react
npm install @zag-js/scroll-area @zag-js/solid # or yarn add @zag-js/scroll-area @zag-js/solid
npm install @zag-js/scroll-area @zag-js/vue # or yarn add @zag-js/scroll-area @zag-js/vue
npm install @zag-js/scroll-area @zag-js/svelte # or yarn add @zag-js/scroll-area @zag-js/svelte
Anatomy
To set up the scroll area correctly, you'll need to understand its anatomy and how we name its parts.
Each part includes a
data-partattribute to help identify them in the DOM.
Required style
It's important to note that the scroll area requires the following styles to be applied to the viewport element to hide the scrollbar:
[data-scope="scroll-area"][data-part="viewport"] { /* hide scrollbar */ scrollbar-width: none; &::-webkit-scrollbar { display: none; } }
Usage
Import the scroll area package:
import * as scrollArea from "@zag-js/scroll-area"
The scroll area package exports two key functions:
machine- State machine logic.connect- Maps machine state to JSX props and event handlers.
Then use the framework integration helpers:
import * as scrollArea from "@zag-js/scroll-area" import { useMachine, normalizeProps } from "@zag-js/react" import { useId } from "react" function ScrollArea() { const service = useMachine(scrollArea.machine, { id: useId(), }) const api = scrollArea.connect(service, normalizeProps) return ( <div {...api.getRootProps()}> <div {...api.getViewportProps()}> <div {...api.getContentProps()}> {/* Your scrollable content here */} </div> </div> <div {...api.getScrollbarProps()}> <div {...api.getThumbProps()} /> </div> </div> ) }
import * as scrollArea from "@zag-js/scroll-area" import { useMachine, normalizeProps } from "@zag-js/solid" import { createMemo, createUniqueId } from "solid-js" function ScrollArea() { const service = useMachine(scrollArea.machine, { id: createUniqueId(), }) const api = createMemo(() => scrollArea.connect(service, normalizeProps)) return ( <div {...api().getRootProps()}> <div {...api().getViewportProps()}> <div {...api().getContentProps()}> {/* Your scrollable content here */} </div> </div> <div {...api().getScrollbarProps()}> <div {...api().getThumbProps()} /> </div> </div> ) }
<script setup> import * as scrollArea from "@zag-js/scroll-area" import { useMachine, normalizeProps } from "@zag-js/vue" import { computed, useId } from "vue" const service = useMachine(scrollArea.machine, { id: useId() }) const api = computed(() => scrollArea.connect(service, normalizeProps)) </script> <template> <div v-bind="api.getRootProps()"> <div v-bind="api.getViewportProps()"> <div v-bind="api.getContentProps()"> <!-- Your scrollable content here --> </div> </div> <div v-bind="api.getScrollbarProps()"> <div v-bind="api.getThumbProps()" /> </div> </div> </template>
<script lang="ts"> import * as scrollArea from "@zag-js/scroll-area" import { useMachine, normalizeProps } from "@zag-js/svelte" const id = $props.id() const service = useMachine(scrollArea.machine, { id }) const api = $derived(scrollArea.connect(service, normalizeProps)) </script> <div {...api.getRootProps()}> <div {...api.getViewportProps()}> <div {...api.getContentProps()}> <!-- Your scrollable content here --> </div> </div> <div {...api.getScrollbarProps()}> <div {...api.getThumbProps()} /> </div> </div>
Programmatic scrolling to edges
You can programmatically scroll to any edge of the scroll area using the
scrollToEdge method:
// Scroll to bottom api.scrollToEdge({ edge: "bottom" }) // Scroll to top with custom easing api.scrollToEdge({ edge: "top", duration: 500, easing: (t) => t * t, })
Programmatic scrolling to coordinates
Use the scrollTo method to scroll to specific coordinates:
// Scroll to specific position api.scrollTo({ top: 100, left: 50, }) // Scroll with smooth behavior api.scrollTo({ top: 200, behavior: "smooth", duration: 300, })
Reading scroll position and overflow
The API provides several properties to check the current scroll state:
// Check if at edges console.log(api.isAtTop) // boolean console.log(api.isAtBottom) // boolean console.log(api.isAtLeft) // boolean console.log(api.isAtRight) // boolean // Check for overflow console.log(api.hasOverflowX) // boolean console.log(api.hasOverflowY) // boolean // Get scroll progress (0-1) const progress = api.getScrollProgress() // { x: number, y: number }
Rendering scrollbars only when needed
Only render scrollbars when there's overflow content:
{ api.hasOverflowY && ( <div {...api.getScrollbarProps({ orientation: "vertical" })}> <div {...api.getThumbProps({ orientation: "vertical" })} /> </div> ) } { api.hasOverflowX && ( <div {...api.getScrollbarProps({ orientation: "horizontal" })}> <div {...api.getThumbProps({ orientation: "horizontal" })} /> </div> ) }
Scrollbar state
Get the current state of a scrollbar for styling purposes:
const verticalState = api.getScrollbarState({ orientation: "vertical" }) // Returns: { hovering: boolean, scrolling: boolean, hidden: boolean }
Styling guide
Each part includes a data-part attribute you can target in CSS.
Scrolling state
When the user is actively scrolling, a data-scrolling attribute is set on the
scrollbar elements:
[data-part="scrollbar"][data-scrolling] { /* styles when actively scrolling */ } [data-part="thumb"][data-scrolling] { /* styles for thumb when scrolling */ }
Hover state
When hovering over the scroll area or scrollbar, a data-hover attribute is
applied:
[data-part="root"][data-hover] { /* styles when hovering over scroll area */ } [data-part="scrollbar"][data-hover] { /* styles when hovering over scrollbar */ }
Hidden state
Scrollbars can be automatically hidden when not needed:
[data-part="scrollbar"][data-hidden] { /* styles for hidden scrollbar */ opacity: 0; pointer-events: none; }
Orientation
Different styles can be applied based on scrollbar orientation:
[data-part="scrollbar"][data-orientation="vertical"] { /* vertical scrollbar styles */ } [data-part="scrollbar"][data-orientation="horizontal"] { /* horizontal scrollbar styles */ }
Methods and Properties
The scroll area's api exposes the following methods and properties:
Machine Context
The scroll area machine exposes the following context properties:
idsPartial<{ root: string; viewport: string; content: string; scrollbar: string; thumb: string; }>The ids of the scroll area elementsdir"ltr" | "rtl"The document's text/writing direction.idstringThe unique identifier of the machine.getRootNode() => ShadowRoot | Node | DocumentA root node to correctly resolve document in custom environments. E.x.: Iframes, Electron.
Machine API
The scroll area api exposes the following methods:
isAtTopbooleanWhether the scroll area is at the topisAtBottombooleanWhether the scroll area is at the bottomisAtLeftbooleanWhether the scroll area is at the leftisAtRightbooleanWhether the scroll area is at the righthasOverflowXbooleanWhether the scroll area has horizontal overflowhasOverflowYbooleanWhether the scroll area has vertical overflowgetScrollProgress() => PointGet the scroll progress as values between 0 and 1scrollToEdge(details: ScrollToEdgeDetails) => voidScroll to the edge of the scroll areascrollTo(details: ScrollToDetails) => voidScroll to specific coordinatesgetScrollbarState(props: ScrollbarProps) => ScrollbarStateReturns the state of the scrollbar