ScrollArea
Area with custom scrollbars
Source
LLM docs
Docs
PackageIcon
Usage
The ScrollArea component supports the following props:
typedefines scrollbars behavior:hover– scrollbars are visible on hoverscroll– scrollbars are visible on scrollauto– similar tooverflow: auto– scrollbars are always visible when the content is overflowingalways– same asauto, but scrollbars are always visible regardless of whether the content is overflowingnever– scrollbars are always hidden
offsetScrollbars– adds padding to offset scrollbars with the following options:true– adds padding to offset both scrollbarsx– adds padding to offset horizontal scrollbar onlyy– adds padding to offset vertical scrollbar onlypresent– adds padding only when scrollbars are visible
scrollbarSize– scrollbar size, controls scrollbar and thumb width/heightscrollHideDelay– delay in ms to hide scrollbars, applicable only when type ishoverorscrolloverscrollBehavior– controls overscroll-behavior of the viewport
Charizard (Pokémon)
Charizard description from Bulbapedia
Charizard is a draconic, bipedal Pokémon. It is primarily orange with a cream underside from the chest to the tip of its tail. It has a long neck, small blue eyes, slightly raised nostrils, and two horn-like structures protruding from the back of its rectangular head. There are two fangs visible in the upper jaw when its mouth is closed. Two large wings with blue-green undersides sprout from its back, and a horn-like appendage juts out from the top of the third joint of each wing. A single wing-finger is visible through the center of each wing membrane. Charizard's arms are short and skinny compared to its robust belly, and each limb has three white claws. It has stocky legs with cream-colored soles on each of its plantigrade feet. The tip of its long, tapering tail burns with a sizable flame.
As Mega Charizard X, its body and legs are more physically fit, though its arms remain thin. Its skin turns black with a sky-blue underside and soles. Two spikes with blue tips curve upward from the front and back of each shoulder, while the tips of its horns sharpen, turn blue, and curve slightly upward. Its brow and claws are larger, and its eyes are now red. It has two small, fin-like spikes under each horn and two more down its lower neck. The finger disappears from the wing membrane, and the lower edges are divided into large, rounded points. The third joint of each wing-arm is adorned with a claw-like spike. Mega Charizard X breathes blue flames out the sides of its mouth, and the flame on its tail now burns blue. It is said that its new power turns it black and creates more intense flames.
import { ScrollArea } from '@mantine/core';
function Demo() {
return (
<ScrollArea h={250}>
{/* ... content */}
</ScrollArea>
);
}Horizontal scrollbars
Charizard (Pokémon)
Charizard description from Bulbapedia
Charizard is a draconic, bipedal Pokémon. It is primarily orange with a cream underside from the chest to the tip of its tail. It has a long neck, small blue eyes, slightly raised nostrils, and two horn-like structures protruding from the back of its rectangular head. There are two fangs visible in the upper jaw when its mouth is closed. Two large wings with blue-green undersides sprout from its back, and a horn-like appendage juts out from the top of the third joint of each wing. A single wing-finger is visible through the center of each wing membrane. Charizard's arms are short and skinny compared to its robust belly, and each limb has three white claws. It has stocky legs with cream-colored soles on each of its plantigrade feet. The tip of its long, tapering tail burns with a sizable flame.
As Mega Charizard X, its body and legs are more physically fit, though its arms remain thin. Its skin turns black with a sky-blue underside and soles. Two spikes with blue tips curve upward from the front and back of each shoulder, while the tips of its horns sharpen, turn blue, and curve slightly upward. Its brow and claws are larger, and its eyes are now red. It has two small, fin-like spikes under each horn and two more down its lower neck. The finger disappears from the wing membrane, and the lower edges are divided into large, rounded points. The third joint of each wing-arm is adorned with a claw-like spike. Mega Charizard X breathes blue flames out the sides of its mouth, and the flame on its tail now burns blue. It is said that its new power turns it black and creates more intense flames.
import { ScrollArea, Box } from '@mantine/core';
function Demo() {
return (
<ScrollArea w={300} h={200}>
<Box w={600}>
{/* ... content */}
</Box>
</ScrollArea>
);
}Disable horizontal scrollbars
To disable horizontal scrollbars, set the scrollbars="y" prop:
Charizard (Pokémon)
Charizard description from Bulbapedia
Charizard is a draconic, bipedal Pokémon. It is primarily orange with a cream underside from the chest to the tip of its tail. It has a long neck, small blue eyes, slightly raised nostrils, and two horn-like structures protruding from the back of its rectangular head. There are two fangs visible in the upper jaw when its mouth is closed. Two large wings with blue-green undersides sprout from its back, and a horn-like appendage juts out from the top of the third joint of each wing. A single wing-finger is visible through the center of each wing membrane. Charizard's arms are short and skinny compared to its robust belly, and each limb has three white claws. It has stocky legs with cream-colored soles on each of its plantigrade feet. The tip of its long, tapering tail burns with a sizable flame.
As Mega Charizard X, its body and legs are more physically fit, though its arms remain thin. Its skin turns black with a sky-blue underside and soles. Two spikes with blue tips curve upward from the front and back of each shoulder, while the tips of its horns sharpen, turn blue, and curve slightly upward. Its brow and claws are larger, and its eyes are now red. It has two small, fin-like spikes under each horn and two more down its lower neck. The finger disappears from the wing membrane, and the lower edges are divided into large, rounded points. The third joint of each wing-arm is adorned with a claw-like spike. Mega Charizard X breathes blue flames out the sides of its mouth, and the flame on its tail now burns blue. It is said that its new power turns it black and creates more intense flames.
import { ScrollArea, Box } from '@mantine/core';
function Demo() {
return (
<ScrollArea w={300} h={200} scrollbars="y">
<Box w={600}>
{/* ... content */}
</Box>
</ScrollArea>
);
}Subscribe to scroll position changes
Set the onScrollPositionChange function to subscribe to scroll position changes.
It will be called each time the user scrolls with x and y coordinates:
Charizard (Pokémon)
Charizard description from Bulbapedia
Charizard is a draconic, bipedal Pokémon. It is primarily orange with a cream underside from the chest to the tip of its tail. It has a long neck, small blue eyes, slightly raised nostrils, and two horn-like structures protruding from the back of its rectangular head. There are two fangs visible in the upper jaw when its mouth is closed. Two large wings with blue-green undersides sprout from its back, and a horn-like appendage juts out from the top of the third joint of each wing. A single wing-finger is visible through the center of each wing membrane. Charizard's arms are short and skinny compared to its robust belly, and each limb has three white claws. It has stocky legs with cream-colored soles on each of its plantigrade feet. The tip of its long, tapering tail burns with a sizable flame.
As Mega Charizard X, its body and legs are more physically fit, though its arms remain thin. Its skin turns black with a sky-blue underside and soles. Two spikes with blue tips curve upward from the front and back of each shoulder, while the tips of its horns sharpen, turn blue, and curve slightly upward. Its brow and claws are larger, and its eyes are now red. It has two small, fin-like spikes under each horn and two more down its lower neck. The finger disappears from the wing membrane, and the lower edges are divided into large, rounded points. The third joint of each wing-arm is adorned with a claw-like spike. Mega Charizard X breathes blue flames out the sides of its mouth, and the flame on its tail now burns blue. It is said that its new power turns it black and creates more intense flames.
Scroll position: { x: 0, y: 0 }
import { useState } from 'react';
import { Text, ScrollArea, Code, Box } from '@mantine/core';
function Demo() {
const [scrollPosition, onScrollPositionChange] = useState({ x: 0, y: 0 });
return (
<>
<ScrollArea
w={300}
h={200}
onScrollPositionChange={onScrollPositionChange}
>
<Box w={600}>
{/* ... content */}
</Box>
</ScrollArea>
<Text>
Scroll position: <Code>{`{ x: ${scrollPosition.x}, y: ${scrollPosition.y} }`}</Code>
</Text>
</>
);
}Scroll boundary callbacks
ScrollArea component supports callbacks that are triggered when scrolling reaches boundaries:
Line 1 - This is a long line that requires horizontal scrolling
Line 2 - This is a long line that requires horizontal scrolling
Line 3 - This is a long line that requires horizontal scrolling
Line 4 - This is a long line that requires horizontal scrolling
Line 5 - This is a long line that requires horizontal scrolling
Line 6 - This is a long line that requires horizontal scrolling
Line 7 - This is a long line that requires horizontal scrolling
Line 8 - This is a long line that requires horizontal scrolling
Line 9 - This is a long line that requires horizontal scrolling
Line 10 - This is a long line that requires horizontal scrolling
Line 11 - This is a long line that requires horizontal scrolling
Line 12 - This is a long line that requires horizontal scrolling
Line 13 - This is a long line that requires horizontal scrolling
Line 14 - This is a long line that requires horizontal scrolling
Line 15 - This is a long line that requires horizontal scrolling
Line 16 - This is a long line that requires horizontal scrolling
Line 17 - This is a long line that requires horizontal scrolling
Line 18 - This is a long line that requires horizontal scrolling
Line 19 - This is a long line that requires horizontal scrolling
Line 20 - This is a long line that requires horizontal scrolling
Line 21 - This is a long line that requires horizontal scrolling
Line 22 - This is a long line that requires horizontal scrolling
Line 23 - This is a long line that requires horizontal scrolling
Line 24 - This is a long line that requires horizontal scrolling
Line 25 - This is a long line that requires horizontal scrolling
Line 26 - This is a long line that requires horizontal scrolling
Line 27 - This is a long line that requires horizontal scrolling
Line 28 - This is a long line that requires horizontal scrolling
Line 29 - This is a long line that requires horizontal scrolling
Line 30 - This is a long line that requires horizontal scrolling
Line 31 - This is a long line that requires horizontal scrolling
Line 32 - This is a long line that requires horizontal scrolling
Line 33 - This is a long line that requires horizontal scrolling
Line 34 - This is a long line that requires horizontal scrolling
Line 35 - This is a long line that requires horizontal scrolling
Line 36 - This is a long line that requires horizontal scrolling
Line 37 - This is a long line that requires horizontal scrolling
Line 38 - This is a long line that requires horizontal scrolling
Line 39 - This is a long line that requires horizontal scrolling
Line 40 - This is a long line that requires horizontal scrolling
Line 41 - This is a long line that requires horizontal scrolling
Line 42 - This is a long line that requires horizontal scrolling
Line 43 - This is a long line that requires horizontal scrolling
Line 44 - This is a long line that requires horizontal scrolling
Line 45 - This is a long line that requires horizontal scrolling
Line 46 - This is a long line that requires horizontal scrolling
Line 47 - This is a long line that requires horizontal scrolling
Line 48 - This is a long line that requires horizontal scrolling
Line 49 - This is a long line that requires horizontal scrolling
Line 50 - This is a long line that requires horizontal scrolling
import { useState } from 'react';
import { Badge, Box, Group, ScrollArea, Stack, Text } from '@mantine/core';
function Demo() {
const [topReached, setTopReached] = useState(0);
const [bottomReached, setBottomReached] = useState(0);
const [leftReached, setLeftReached] = useState(0);
const [rightReached, setRightReached] = useState(0);
return (
<Stack align="center">
<Group>
<Badge color="blue">Top: {topReached}</Badge>
<Badge color="green">Bottom: {bottomReached}</Badge>
<Badge color="orange">Left: {leftReached}</Badge>
<Badge color="grape">Right: {rightReached}</Badge>
</Group>
<ScrollArea
h={200}
w={300}
onTopReached={() => setTopReached((c) => c + 1)}
onBottomReached={() => setBottomReached((c) => c + 1)}
onLeftReached={() => setLeftReached((c) => c + 1)}
onRightReached={() => setRightReached((c) => c + 1)}
>
<Box w={600}>
{Array(50)
.fill(0)
.map((_, i) => (
<Text key={i}>
Line {i + 1} - This is a long line that requires horizontal scrolling
</Text>
))}
</Box>
</ScrollArea>
</Stack>
);
}Scroll to position
To programmatically scroll to any position,
get the viewport element ref with the viewportRef prop and call the scrollTo method:
Charizard (Pokémon)
Charizard description from Bulbapedia
Charizard is a draconic, bipedal Pokémon. It is primarily orange with a cream underside from the chest to the tip of its tail. It has a long neck, small blue eyes, slightly raised nostrils, and two horn-like structures protruding from the back of its rectangular head. There are two fangs visible in the upper jaw when its mouth is closed. Two large wings with blue-green undersides sprout from its back, and a horn-like appendage juts out from the top of the third joint of each wing. A single wing-finger is visible through the center of each wing membrane. Charizard's arms are short and skinny compared to its robust belly, and each limb has three white claws. It has stocky legs with cream-colored soles on each of its plantigrade feet. The tip of its long, tapering tail burns with a sizable flame.
As Mega Charizard X, its body and legs are more physically fit, though its arms remain thin. Its skin turns black with a sky-blue underside and soles. Two spikes with blue tips curve upward from the front and back of each shoulder, while the tips of its horns sharpen, turn blue, and curve slightly upward. Its brow and claws are larger, and its eyes are now red. It has two small, fin-like spikes under each horn and two more down its lower neck. The finger disappears from the wing membrane, and the lower edges are divided into large, rounded points. The third joint of each wing-arm is adorned with a claw-like spike. Mega Charizard X breathes blue flames out the sides of its mouth, and the flame on its tail now burns blue. It is said that its new power turns it black and creates more intense flames.
import { useRef } from 'react';
import { ScrollArea, Button, Stack, Group } from '@mantine/core';
function Demo() {
const viewport = useRef<HTMLDivElement>(null);
const scrollToBottom = () =>
viewport.current!.scrollTo({ top: viewport.current!.scrollHeight, behavior: 'smooth' });
const scrollToCenter = () =>
viewport.current!.scrollTo({ top: viewport.current!.scrollHeight / 2, behavior: 'smooth' });
const scrollToTop = () => viewport.current!.scrollTo({ top: 0, behavior: 'smooth' });
return (
<Stack align="center">
<ScrollArea w={300} h={200} viewportRef={viewport}>
{/* ... content */}
</ScrollArea>
<Group justify="center">
<Button onClick={scrollToBottom}>Scroll to bottom</Button>
<Button onClick={scrollToCenter}>Scroll to center</Button>
<Button onClick={scrollToTop}>Scroll to top</Button>
</Group>
</Stack>
);
}Styles API
Charizard (Pokémon)
Charizard description from Bulbapedia
Charizard is a draconic, bipedal Pokémon. It is primarily orange with a cream underside from the chest to the tip of its tail. It has a long neck, small blue eyes, slightly raised nostrils, and two horn-like structures protruding from the back of its rectangular head. There are two fangs visible in the upper jaw when its mouth is closed. Two large wings with blue-green undersides sprout from its back, and a horn-like appendage juts out from the top of the third joint of each wing. A single wing-finger is visible through the center of each wing membrane. Charizard's arms are short and skinny compared to its robust belly, and each limb has three white claws. It has stocky legs with cream-colored soles on each of its plantigrade feet. The tip of its long, tapering tail burns with a sizable flame.
As Mega Charizard X, its body and legs are more physically fit, though its arms remain thin. Its skin turns black with a sky-blue underside and soles. Two spikes with blue tips curve upward from the front and back of each shoulder, while the tips of its horns sharpen, turn blue, and curve slightly upward. Its brow and claws are larger, and its eyes are now red. It has two small, fin-like spikes under each horn and two more down its lower neck. The finger disappears from the wing membrane, and the lower edges are divided into large, rounded points. The third joint of each wing-arm is adorned with a claw-like spike. Mega Charizard X breathes blue flames out the sides of its mouth, and the flame on its tail now burns blue. It is said that its new power turns it black and creates more intense flames.
import { ScrollArea, Box } from '@mantine/core';
import classes from './Demo.module.css';
function Demo() {
return (
<ScrollArea w={300} h={200} type="always" offsetScrollbars classNames={classes}>
<Box w={600}>
{/* ... content */}
</Box>
</ScrollArea>
);
}Scroll element into view
import { useState, useRef } from 'react';
import { ScrollArea, UnstyledButton, TextInput } from '@mantine/core';
const groceries: string[] = [
'🍎 Apples',
'🍌 Bananas',
'🍊 Oranges',
'🥛 Milk',
'🍞 Bread',
'🥚 Eggs',
'🍗 Chicken',
'🥩 Beef',
'🍝 Pasta',
'🍚 Rice',
'🥔 Potatoes',
'🧅 Onions',
'🍅 Tomatoes',
'🥒 Cucumbers',
'🥕 Carrots',
'🥬 Lettuce',
'🍃 Spinach',
'🥦 Broccoli',
'🧀 Cheese',
'🍦 Yogurt',
'🧈 Butter',
'🍚 Sugar',
'🧂 Salt',
'🌶️ Pepper',
'☕ Coffee',
'🍵 Tea',
'🥤 Juice',
'💧 Water',
'🍪 Cookies',
'🍫 Chocolate',
];
function Demo() {
const viewportRef = useRef<HTMLDivElement>(null);
const [query, setQuery] = useState('');
const [hovered, setHovered] = useState(-1);
const filtered = groceries.filter((item) => item.toLowerCase().includes(query.toLowerCase()));
const items = filtered.map((item, index) => (
<UnstyledButton
data-list-item
key={item}
display="block"
bg={index === hovered ? 'var(--mantine-color-blue-light)' : undefined}
w="100%"
p={5}
>
{item}
</UnstyledButton>
));
return (
<>
<TextInput
value={query}
onChange={(event) => {
setQuery(event.currentTarget.value);
setHovered(-1);
}}
onKeyDown={(event) => {
if (event.key === 'ArrowDown') {
event.preventDefault();
setHovered((current) => {
const nextIndex = current + 1 >= filtered.length ? current : current + 1;
viewportRef.current
?.querySelectorAll('[data-list-item]')
?.[nextIndex]?.scrollIntoView({ block: 'nearest' });
return nextIndex;
});
}
if (event.key === 'ArrowUp') {
event.preventDefault();
setHovered((current) => {
const nextIndex = current - 1 < 0 ? current : current - 1;
viewportRef.current
?.querySelectorAll('[data-list-item]')
?.[nextIndex]?.scrollIntoView({ block: 'nearest' });
return nextIndex;
});
}
}}
placeholder="Search groceries"
/>
<ScrollArea h={150} type="always" mt="md" viewportRef={viewportRef}>
{items}
</ScrollArea>
</>
);
}ScrollArea.Autosize
ScrollArea.Autosize component allows to create scrollable containers when given max-height is reached.
It also supports a callback for detecting when vertical overflow occurs:
- onOverflowChange – triggered when content exceeds max-height, making the container scrollable or not
Lorem ipsum, dolor sit amet consectetur adipisicing elit. Dicta perspiciatis reiciendis voluptate eaque itaque quos. Natus iure tenetur libero, reprehenderit ad, sequi, in aliquam eos necessitatibus expedita delectus veniam culpa!
Lorem ipsum, dolor sit amet consectetur adipisicing elit. Dicta perspiciatis reiciendis voluptate eaque itaque quos. Natus iure tenetur libero, reprehenderit ad, sequi, in aliquam eos necessitatibus expedita delectus veniam culpa!
Lorem ipsum, dolor sit amet consectetur adipisicing elit. Dicta perspiciatis reiciendis voluptate eaque itaque quos. Natus iure tenetur libero, reprehenderit ad, sequi, in aliquam eos necessitatibus expedita delectus veniam culpa!
import { useCounter } from '@mantine/hooks';
import { ScrollArea, Button, Group } from '@mantine/core';
const lorem =
'Lorem ipsum, dolor sit amet consectetur adipisicing elit. Dicta perspiciatis reiciendis voluptate eaque itaque quos. Natus iure tenetur libero, reprehenderit ad, sequi, in aliquam eos necessitatibus expedita delectus veniam culpa!';
function Demo() {
const [count, handlers] = useCounter(3, { min: 0, max: 10 });
const content = Array(count)
.fill(0)
.map((_, index) => <p key={index}>{lorem}</p>);
return (
<>
<ScrollArea.Autosize mah={300} maw={400} mx="auto">
{content}
</ScrollArea.Autosize>
<Group justify="center" mt="md">
<Button color="red" onClick={handlers.decrement}>
Remove paragraph
</Button>
<Button onClick={handlers.increment}>
Add paragraph
</Button>
</Group>
</>
);
}ScrollArea.Autosize with Popover
import { useState, useRef } from 'react';
import { ScrollArea, Popover, TextInput, UnstyledButton, Text, Box } from '@mantine/core';
const groceries = [
'Apples',
'Bananas',
'Oranges',
'Milk',
'Bread',
'Eggs',
'Chicken',
'Beef',
'Pasta',
'Rice',
'Potatoes',
'Onions',
'Tomatoes',
'Cucumbers',
'Carrots',
'Lettuce',
'Spinach',
'Broccoli',
'Cheese',
'Yogurt',
'Butter',
'Sugar',
'Salt',
'Pepper',
'Coffee',
'Tea',
'Juice',
'Water',
'Cookies',
'Chocolate',
];
function Demo() {
const viewportRef = useRef<HTMLDivElement>(null);
const [query, setQuery] = useState('');
const [opened, setOpened] = useState(false);
const [hovered, setHovered] = useState(-1);
const filtered = groceries.filter((item) => item.toLowerCase().includes(query.toLowerCase()));
const items = filtered.map((item, index) => (
<UnstyledButton
data-list-item
key={item}
display="block"
bg={index === hovered ? 'var(--mantine-color-blue-light)' : undefined}
w="100%"
p={5}
>
{item}
</UnstyledButton>
));
return (
<Popover width="target" opened={opened}>
<Popover.Target>
<TextInput
value={query}
onFocus={() => setOpened(true)}
onBlur={() => setOpened(false)}
onChange={(event) => {
setQuery(event.currentTarget.value);
setHovered(-1);
}}
onKeyDown={(event) => {
if (event.key === 'ArrowDown') {
event.preventDefault();
setHovered((current) => {
const nextIndex = current + 1 >= filtered.length ? current : current + 1;
viewportRef.current
?.querySelectorAll('[data-list-item]')
?.[nextIndex]?.scrollIntoView({ block: 'nearest' });
return nextIndex;
});
}
if (event.key === 'ArrowUp') {
event.preventDefault();
setHovered((current) => {
const nextIndex = current - 1 < 0 ? current : current - 1;
viewportRef.current
?.querySelectorAll('[data-list-item]')
?.[nextIndex]?.scrollIntoView({ block: 'nearest' });
return nextIndex;
});
}
}}
placeholder="Search groceries"
/>
</Popover.Target>
<Popover.Dropdown p={0}>
<ScrollArea.Autosize viewportRef={viewportRef} mah={200} type="always" scrollbars="y">
<Box px="xs" py={5}>
{items.length > 0 ? items : <Text c="dimmed">Nothing found</Text>}
</Box>
</ScrollArea.Autosize>
</Popover.Dropdown>
</Popover>
);
}