import { ScrollArea } from "@chakra-ui/react"
import LoremIpsum from "react-lorem-ipsum"
export const ScrollAreaBasic = () => (
<ScrollArea.Root height="8.5rem" maxW="lg">
<ScrollArea.Viewport>
<ScrollArea.Content spaceY="4" textStyle="sm">
<LoremIpsum p={3} />
</ScrollArea.Content>
</ScrollArea.Viewport>
<ScrollArea.Scrollbar>
<ScrollArea.Thumb />
</ScrollArea.Scrollbar>
<ScrollArea.Corner />
</ScrollArea.Root>
)
用法
¥Usage
import { ScrollArea } from "@chakra-ui/react"
<ScrollArea.Root>
<ScrollArea.Viewport>
<ScrollArea.Content />
</ScrollArea.Viewport>
<ScrollArea.Scrollbar>
<ScrollArea.Thumb />
</ScrollArea.Scrollbar>
<ScrollArea.Corner />
</ScrollArea.Root>
示例
¥Examples
变量
¥Variants
使用 variant
属性更改滚动条的可见性行为。值可以是 hover
(默认)或 always
。
¥Use the variant
prop to change the scrollbar visibility behavior. Values can
be either hover
(default) or always
.
variant="hover"
variant="always"
import { For, ScrollArea, Stack, Text } from "@chakra-ui/react"
import Lorem from "react-lorem-ipsum"
export const ScrollAreaWithVariants = () => (
<Stack gap="8" maxW="lg">
<For each={["hover", "always"]}>
{(variant) => (
<Stack gap="2" key={variant}>
<Text fontWeight="medium">variant="{variant}"</Text>
<ScrollArea.Root height="8rem" variant={variant}>
<ScrollArea.Viewport>
<ScrollArea.Content paddingEnd="3" textStyle="sm">
<Lorem p={4} />
</ScrollArea.Content>
</ScrollArea.Viewport>
<ScrollArea.Scrollbar />
</ScrollArea.Root>
</Stack>
)}
</For>
</Stack>
)
尺寸
¥Sizes
使用 size
属性更改滚动区域的大小。这会影响滚动条的粗细和内容填充。
¥Use the size
prop to change the size of the scroll area. This affects the
scrollbar thickness and content padding.
size="xs"
size="sm"
size="md"
size="lg"
import { For, ScrollArea, Stack, Text } from "@chakra-ui/react"
import LoremIpsum from "react-lorem-ipsum"
export const ScrollAreaWithSizes = () => (
<Stack gap="8" maxW="lg">
<For each={["xs", "sm", "md", "lg"]}>
{(size) => (
<Stack gap="2" key={size}>
<Text fontWeight="medium">size="{size}"</Text>
<ScrollArea.Root size={size} height="8rem" variant="always">
<ScrollArea.Viewport>
<ScrollArea.Content paddingEnd="5" textStyle="sm">
<LoremIpsum p={2} />
</ScrollArea.Content>
</ScrollArea.Viewport>
<ScrollArea.Scrollbar />
</ScrollArea.Root>
</Stack>
)}
</For>
</Stack>
)
水平滚动
¥Horizontal Scrolling
当内容水平溢出时,滚动区域会自动支持水平滚动。
¥The scroll area automatically supports horizontal scrolling when content overflows horizontally.
import { Flex, ScrollArea } from "@chakra-ui/react"
import { DecorativeBox } from "compositions/lib/decorative-box"
export const ScrollAreaHorizontal = () => (
<ScrollArea.Root width="24rem" size="xs">
<ScrollArea.Viewport>
<ScrollArea.Content py="4">
<Flex gap="4" flexWrap="nowrap">
{Array.from({ length: 12 }, (_, i) => (
<DecorativeBox rounded="sm" key={i} h="20" w="40" flexShrink="0">
Item {i + 1}
</DecorativeBox>
))}
</Flex>
</ScrollArea.Content>
</ScrollArea.Viewport>
<ScrollArea.Scrollbar orientation="horizontal" />
<ScrollArea.Corner />
</ScrollArea.Root>
)
双向
¥Both Directions
当内容在两个方向上溢出时,两个滚动条都会显示。
¥When content overflows in both directions, both scrollbars will appear.
import { ScrollArea } from "@chakra-ui/react"
import LoremIpsum from "react-lorem-ipsum"
export const ScrollAreaBothDirections = () => (
<ScrollArea.Root height="12rem" width="lg" size="xs" p="2">
<ScrollArea.Viewport>
<ScrollArea.Content spaceY="4" w="40rem" textStyle="sm">
<LoremIpsum p={3} />
</ScrollArea.Content>
</ScrollArea.Viewport>
<ScrollArea.Scrollbar orientation="horizontal" />
<ScrollArea.Scrollbar orientation="vertical" />
<ScrollArea.Corner bg="bg" />
</ScrollArea.Root>
)
你可以渲染 ScrollArea.Corner
组件,使其显示一个角指示器,填充两个滚动条的交叉点,从而实现无缝且富有样式的外观。
¥You can render the ScrollArea.Corner
component to show a corner indicator to
fill the intersection of the two scrollbars for a seamless, styled appearance.
滚动阴影
¥Scroll Shadow
通过使用 mask-image
在边缘显示滚动阴影,在内容可滚动时添加视觉反馈。
¥Add visual feedback when content is scrollable by implementing scroll shadows
that appear at the edges using mask-image
.
import { ScrollArea } from "@chakra-ui/react"
import { DecorativeBox } from "compositions/lib/decorative-box"
const Demo = () => {
return (
<ScrollArea.Root height="20rem" maxW="lg">
<ScrollArea.Viewport
css={{
"--scroll-shadow-size": "4rem",
maskImage:
"linear-gradient(#000,#000,transparent 0,#000 var(--scroll-shadow-size),#000 calc(100% - var(--scroll-shadow-size)),transparent)",
"&[data-at-top]": {
maskImage:
"linear-gradient(180deg,#000 calc(100% - var(--scroll-shadow-size)),transparent)",
},
"&[data-at-bottom]": {
maskImage:
"linear-gradient(0deg,#000 calc(100% - var(--scroll-shadow-size)),transparent)",
},
}}
>
<ScrollArea.Content spaceY="4">
{Array.from({ length: 10 }, (_, i) => (
<DecorativeBox key={i} h="20">
Item {i + 1}
</DecorativeBox>
))}
</ScrollArea.Content>
</ScrollArea.Viewport>
</ScrollArea.Root>
)
}
缩略图样式
¥Thumb Styling
使用不同的样式和颜色自定义滚动条缩略图的外观。
¥Customize the appearance of the scrollbar thumb with different styles and colors.
import { ScrollArea } from "@chakra-ui/react"
import LoremIpsum from "react-lorem-ipsum"
export const ScrollAreaWithThumbStyling = () => (
<ScrollArea.Root height="8rem" maxW="2xl" variant="always">
<ScrollArea.Viewport>
<ScrollArea.Content spaceY="4" pe="2">
<LoremIpsum p={2} />
</ScrollArea.Content>
</ScrollArea.Viewport>
<ScrollArea.Scrollbar bg="red.subtle">
<ScrollArea.Thumb bg="red.solid" />
</ScrollArea.Scrollbar>
</ScrollArea.Root>
)
贴在底部
¥Stick to Bottom
实现类似聊天的行为,使新内容自动滚动到底部,但允许手动滚动。
¥Implement chat-like behavior where new content automatically scrolls to the bottom, but allows manual scrolling.
本示例使用 use-stick-to-bottom
组件实现滚动固定。
"use client"
import {
Box,
Button,
ButtonGroup,
IconButton,
ScrollArea,
VStack,
} from "@chakra-ui/react"
import { DecorativeBox } from "compositions/lib/decorative-box"
import { useState } from "react"
import { LuArrowDown } from "react-icons/lu"
import { useStickToBottom } from "use-stick-to-bottom"
const Demo = () => {
const sticky = useStickToBottom()
const [messages, setMessages] = useState<string[]>([
"Message 1 - 10:00:00",
"Message 2 - 10:00:01",
"Message 3 - 10:00:02",
"Message 4 - 10:00:03",
"Message 5 - 10:00:04",
"Message 6 - 10:00:05",
"Message 7 - 10:00:06",
"Message 8 - 10:00:07",
"Message 9 - 10:00:08",
"Message 10 - 10:00:09",
])
const addMessage = () => {
const newMessage = `Message ${messages.length + 1} - ${new Date().toLocaleTimeString()}`
setMessages((prev) => [...prev, newMessage])
}
const addMultipleMessages = () => {
const newMessages = Array.from(
{ length: 5 },
(_, i) =>
`Batch message ${messages.length + i + 1} - ${new Date().toLocaleTimeString()}`,
)
setMessages((prev) => [...prev, ...newMessages])
}
const removeMessage = () => {
setMessages((prev) => prev.slice(0, -1))
}
return (
<VStack gap="4" align="stretch" width="20rem">
<ButtonGroup gap="2" size="sm" variant="outline">
<Button onClick={addMessage}>Add Message</Button>
<Button onClick={addMultipleMessages}>Add 5 Messages</Button>
<Button onClick={removeMessage}>Remove Message</Button>
</ButtonGroup>
<ScrollArea.Root
maxHeight="20rem"
width="full"
borderWidth="1px"
rounded="l2"
size="xs"
>
<ScrollArea.Viewport ref={sticky.scrollRef}>
<ScrollArea.Content ref={sticky.contentRef}>
<VStack gap="2" p="3" align="stretch">
{messages.map((message, index) => (
<DecorativeBox key={index} h="12">
{message}
</DecorativeBox>
))}
</VStack>
</ScrollArea.Content>
</ScrollArea.Viewport>
<ScrollArea.Scrollbar />
{!sticky.isAtBottom && (
<Box position="absolute" bottom="4" right="4" zIndex="10">
<IconButton
size="sm"
onClick={() => {
sticky.scrollToBottom()
}}
colorScheme="blue"
variant="solid"
>
<LuArrowDown />
</IconButton>
</Box>
)}
</ScrollArea.Root>
</VStack>
)
}
虚拟化
¥Virtualization
使用 @tanstack/react-virtual
仅渲染可见项目,高效处理大型数据集。
¥Handle large datasets efficiently by rendering only visible items using
@tanstack/react-virtual
.
"use client"
import { ScrollArea } from "@chakra-ui/react"
import { type VirtualItem, useVirtualizer } from "@tanstack/react-virtual"
import { DecorativeBox } from "compositions/lib/decorative-box"
import React, { useCallback, useMemo, useRef } from "react"
const Demo = () => {
const scrollRef = useRef<HTMLDivElement>(null)
const items = useMemo(
() =>
Array.from({ length: 1000 }, (_, i) => ({
id: i,
name: `Item ${i + 1}`,
})),
[],
)
const virtualizer = useVirtualizer({
count: items.length,
getScrollElement: () => scrollRef.current,
estimateSize: () => 80,
overscan: 5,
})
const contentProps = useMemo(
(): React.ComponentProps<"div"> => ({
style: {
height: `${virtualizer.getTotalSize()}px`,
width: "full",
position: "relative",
},
}),
[virtualizer],
)
const getItemProps = useCallback(
(item: VirtualItem): React.ComponentProps<"div"> => ({
style: {
position: "absolute",
top: 0,
left: 0,
width: "100%",
paddingBottom: 4,
height: `${item.size}px`,
transform: `translateY(${item.start}px)`,
},
}),
[],
)
return (
<ScrollArea.Root height="20rem" maxWidth="xl">
<ScrollArea.Viewport ref={scrollRef}>
<ScrollArea.Content {...contentProps}>
{virtualizer.getVirtualItems().map((virtualItem) => {
const item = items[virtualItem.index]
return (
<div key={virtualItem.key} {...getItemProps(virtualItem)}>
<DecorativeBox w="full">{item.name}</DecorativeBox>
</div>
)
})}
</ScrollArea.Content>
</ScrollArea.Viewport>
<ScrollArea.Scrollbar bg="transparent" />
</ScrollArea.Root>
)
}
商店
¥Store
将滚动区域与外部状态管理和编程控制结合使用。
¥Use the scroll area with external state management and programmatic control.
"use client"
import { ScrollArea, useScrollArea } from "@chakra-ui/react"
import LoremIpsum from "react-lorem-ipsum"
const Demo = () => {
const scrollArea = useScrollArea()
return (
<ScrollArea.RootProvider value={scrollArea} height="8.5rem">
<ScrollArea.Viewport>
<ScrollArea.Content spaceY="4">
<LoremIpsum p={3} />
</ScrollArea.Content>
</ScrollArea.Viewport>
<ScrollArea.Scrollbar>
<ScrollArea.Thumb />
</ScrollArea.Scrollbar>
</ScrollArea.RootProvider>
)
}
滚动到侧边
¥Scroll to Side
通过滚动到不同的侧面和方向,以编程方式浏览内容。
¥Programmatically navigate through content by scrolling to different sides and directions.
"use client"
import {
Button,
ButtonGroup,
ScrollArea,
Stack,
useScrollArea,
} from "@chakra-ui/react"
import LoremIpsum from "react-lorem-ipsum"
const Demo = () => {
const scrollArea = useScrollArea()
return (
<Stack gap="8" align="flex-start" maxW="xl">
<ButtonGroup variant="outline" justify="center" size="sm">
<Button
onClick={() =>
scrollArea.scrollToEdge({ edge: "bottom", behavior: "smooth" })
}
>
Scroll to bottom
</Button>
<Button
onClick={() =>
scrollArea.scrollToEdge({ edge: "top", behavior: "smooth" })
}
>
Scroll to top
</Button>
</ButtonGroup>
<ScrollArea.RootProvider value={scrollArea} height="8rem" width="24rem">
<ScrollArea.Viewport>
<ScrollArea.Content>
<LoremIpsum p={3} />
</ScrollArea.Content>
</ScrollArea.Viewport>
<ScrollArea.Scrollbar />
</ScrollArea.RootProvider>
</Stack>
)
}
滚动到位置
¥Scroll to Position
使用流畅的动画跳转到可滚动区域内的特定位置或项目。
¥Jump to specific positions or items within the scrollable area with smooth animations.
"use client"
import { Button, ScrollArea, Stack, useScrollArea } from "@chakra-ui/react"
import LoremIpsum from "react-lorem-ipsum"
const Demo = () => {
const scrollArea = useScrollArea()
return (
<Stack gap="8" align="flex-start" maxW="xl">
<Button
variant="outline"
size="sm"
onClick={() => scrollArea.scrollTo({ top: 200, behavior: "smooth" })}
>
Scroll to 100px
</Button>
<ScrollArea.RootProvider value={scrollArea} height="8rem" width="24rem">
<ScrollArea.Viewport>
<ScrollArea.Content>
<LoremIpsum p={3} />
</ScrollArea.Content>
</ScrollArea.Viewport>
<ScrollArea.Scrollbar />
</ScrollArea.RootProvider>
</Stack>
)
}
RTL 支持
¥RTL Support
滚动区域组件完全支持从右到左 (RTL) 的语言,例如阿拉伯语和希伯来语。
¥The scroll area component fully supports Right-to-Left (RTL) languages like Arabic and Hebrew.
import { LocaleProvider, ScrollArea } from "@chakra-ui/react"
const arabicText = [
"مرحباً بكم في نظام التمرير المخصص",
"هذا مثال على النص العربي في منطقة التمرير",
"يدعم النظام اللغات التي تُكتب من اليمين إلى اليسار",
"التمرير الأفقي يعمل بشكل صحيح مع النصوص العربية",
"يمكنك رؤية كيف تتكيف أشرطة التمرير مع اتجاه النص",
"النظام يدعم التمرير العمودي والأفقي في نفس الوقت",
"يمكن تخصيص مظهر أشرطة التمرير حسب التصميم المطلوب",
"التفاعل مع أشرطة التمرير سهل ومريح للمستخدم",
"يعمل النظام بسلاسة على جميع المتصفحات الحديثة",
"يمكن دمج هذا المكون مع مكونات أخرى بسهولة",
"الأداء محسّن للتعامل مع كميات كبيرة من المحتوى",
]
const Demo = () => {
return (
<LocaleProvider locale="ar-AE">
<ScrollArea.Root height="8rem" width="24rem" size="sm">
<ScrollArea.Viewport>
<ScrollArea.Content p="2">
{arabicText.map((text, i) => (
<span key={i}>{text}</span>
))}
</ScrollArea.Content>
</ScrollArea.Viewport>
<ScrollArea.Scrollbar />
<ScrollArea.Corner />
</ScrollArea.Root>
</LocaleProvider>
)
}
带菜单
¥With Menu
将滚动区域与其他组件(例如菜单)结合使用,以处理溢出内容。此示例演示如何创建包含多个项目的可滚动菜单。
¥Combine scroll area with other components like menus to handle overflowing content. This example shows how to create a scrollable menu with many items.
import { Button, Menu, Portal, ScrollArea } from "@chakra-ui/react"
import { useId } from "react"
const Demo = () => {
const contentId = useId()
return (
<Menu.Root ids={{ content: contentId }}>
<Menu.Trigger asChild>
<Button variant="outline" size="sm">
Open Menu with Scroll
</Button>
</Menu.Trigger>
<Portal>
<Menu.Positioner>
<MenuContent maxH="80" w="64" id={contentId}>
{menuItems.map((item) => (
<Menu.Item key={item.value} value={item.value}>
{item.label}
</Menu.Item>
))}
</MenuContent>
</Menu.Positioner>
</Portal>
</Menu.Root>
)
}
function MenuContent(props: Menu.ContentProps) {
const { id, children, ...rest } = props
return (
<ScrollArea.Root overflow="visible" ids={{ viewport: id }}>
<ScrollArea.Viewport asChild>
<Menu.Content {...rest}>
{children}
<ScrollArea.Scrollbar bg="transparent">
<ScrollArea.Thumb />
</ScrollArea.Scrollbar>
</Menu.Content>
</ScrollArea.Viewport>
</ScrollArea.Root>
)
}
const menuItems = [
{ value: "profile", label: "Profile" },
{ value: "settings", label: "Settings" },
{ value: "notifications", label: "Notifications" },
{ value: "messages", label: "Messages" },
{ value: "documents", label: "Documents" },
{ value: "files", label: "Files" },
{ value: "images", label: "Images" },
{ value: "videos", label: "Videos" },
{ value: "music", label: "Music" },
{ value: "downloads", label: "Downloads" },
{ value: "share", label: "Share" },
{ value: "copy", label: "Copy" },
{ value: "edit", label: "Edit" },
{ value: "favorites", label: "Favorites" },
{ value: "liked", label: "Liked Items" },
{ value: "bookmarks", label: "Bookmarks" },
{ value: "flagged", label: "Flagged Items" },
{ value: "help", label: "Help & Support" },
{ value: "trash", label: "Trash" },
{ value: "logout", label: "Logout" },
]
属性
¥Props
根元素
¥Root
Prop | Default | Type |
---|---|---|
colorPalette | 'gray' | 'gray' | 'red' | 'orange' | 'yellow' | 'green' | 'teal' | 'blue' | 'cyan' | 'purple' | 'pink' The color palette of the component |
variant | 'hover' | 'hover' | 'always' The variant of the component |
size | 'md' | 'xs' | 'sm' | 'md' | 'lg' The size of the component |
as | React.ElementType The underlying element to render. | |
asChild | boolean Use the provided child element as the default rendered element, combining their props and behavior. For more details, read our Composition guide. | |
unstyled | boolean Whether to remove the component's style. | |
ids | Partial<{ root: string; viewport: string; content: string; scrollbar: string; thumb: string }> The ids of the scroll area elements |
视口
¥Viewport
Prop | Default | Type |
---|---|---|
as | React.ElementType The underlying element to render. | |
asChild | boolean Use the provided child element as the default rendered element, combining their props and behavior. For more details, read our Composition guide. |
内容
¥Content
Prop | Default | Type |
---|---|---|
as | React.ElementType The underlying element to render. | |
asChild | boolean Use the provided child element as the default rendered element, combining their props and behavior. For more details, read our Composition guide. |
滚动条
¥Scrollbar
Prop | Default | Type |
---|---|---|
as | React.ElementType The underlying element to render. | |
asChild | boolean Use the provided child element as the default rendered element, combining their props and behavior. For more details, read our Composition guide. | |
orientation | Orientation |
缩略图
¥Thumb
Prop | Default | Type |
---|---|---|
as | React.ElementType The underlying element to render. | |
asChild | boolean Use the provided child element as the default rendered element, combining their props and behavior. For more details, read our Composition guide. |
角落
¥Corner
Prop | Default | Type |
---|---|---|
as | React.ElementType The underlying element to render. | |
asChild | boolean Use the provided child element as the default rendered element, combining their props and behavior. For more details, read our Composition guide. |