Skip to main content
View Zag.js on Github
Join the Discord server

Scroll Area

A scroll area provides a scrollable viewport with customizable scrollbars for content that exceeds the container's dimensions.

Features

  • Custom scrollbar styling and behavior
  • Smooth scrolling with easing functions
  • Touch and keyboard navigation support
  • Automatic scrollbar hiding when not needed
  • Nested scroll area support
  • Programmatic scrolling to edges or coordinates

Installation

To use the scroll area machine in your project, run the following command in your command line:

npm install @zag-js/scroll-area @zag-js/react # or yarn add @zag-js/scroll-area @zag-js/react

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-part attribute 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

First, import the scroll area package into your project

import * as scrollArea from "@zag-js/scroll-area"

The scroll area package exports two key functions:

  • machine — The state machine logic for the scroll area widget.
  • connect — The function that translates the machine's state to JSX attributes and event handlers.

You'll also need to provide a unique id to the useMachine hook. This is used to ensure that every part has a unique identifier.

Next, import the required hooks and functions for your framework and use the scroll area machine in your project 🔥

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> ) }

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, })

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, })

Checking scroll position

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 }

Conditional scrollbar rendering

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 }

Nested scroll areas

Scroll areas can be nested within each other for complex layouts:

<div {...api.getRootProps()}> <div {...api.getViewportProps()}> <div {...api.getContentProps()}> {/* Nested scroll area */} <ScrollAreaComponent style={{ maxHeight: 200 }}> {/* Inner content */} </ScrollAreaComponent> </div> </div> </div>

Styling guide

Earlier, we mentioned that each scroll area part has a data-part attribute added to them to select and style them in the DOM.

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 elements
  • dir"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 top
  • isAtBottombooleanWhether the scroll area is at the bottom
  • isAtLeftbooleanWhether the scroll area is at the left
  • isAtRightbooleanWhether the scroll area is at the right
  • hasOverflowXbooleanWhether the scroll area has horizontal overflow
  • hasOverflowYbooleanWhether the scroll area has vertical overflow
  • getScrollProgress() => PointGet the scroll progress as values between 0 and 1
  • scrollToEdge(details: ScrollToEdgeDetails) => voidScroll to the edge of the scroll area
  • scrollTo(details: ScrollToDetails) => voidScroll to specific coordinates
  • getScrollbarState(props: ScrollbarProps) => ScrollbarStateReturns the state of the scrollbar

Data Attributes

Root
data-scope
scroll-area
data-part
root
data-overflow-x
Present when the content overflows the x-axis
data-overflow-y
Present when the content overflows the y-axis
Viewport
data-scope
scroll-area
data-part
viewport
data-at-top
Present when scrolled to the top edge
data-at-bottom
Present when scrolled to the bottom edge
data-at-left
Present when scrolled to the left edge
data-at-right
Present when scrolled to the right edge
data-overflow-x
Present when the content overflows the x-axis
data-overflow-y
Present when the content overflows the y-axis
Content
data-scope
scroll-area
data-part
content
data-overflow-x
Present when the content overflows the x-axis
data-overflow-y
Present when the content overflows the y-axis
Scrollbar
data-scope
scroll-area
data-part
scrollbar
data-orientation
The orientation of the scrollbar
data-scrolling
Present when scrolling
data-hover
Present when hovering
data-overflow-x
Present when the content overflows the x-axis
data-overflow-y
Present when the content overflows the y-axis
Thumb
data-scope
scroll-area
data-part
thumb
data-orientation
The orientation of the thumb
Corner
data-scope
scroll-area
data-part
corner
data-state
"hidden" | "visible"
data-overflow-x
Present when the content overflows the x-axis
data-overflow-y
Present when the content overflows the y-axis

CSS Variables

Root
--corner-width
The width of the Root
--corner-height
The height of the Root
--thumb-width
The width of the slider thumb
--thumb-height
The height of the slider thumb

Edit this page on GitHub

A project by Chakra Systems
Copyright © 2025
On this page