7.16.0
mantinedev/mantine7.16.0Jan 14, 2025by rtivital
AI Summary
A major feature release introducing scroll tracking capabilities, new UI components like TableOfContents and ClearButton, and enhancements to Carousel and form interactions.
Key Highlights
- Introduced `use-scroll-spy` hook and `TableOfContents` component for documentation navigation
- Added `Input.ClearButton` component and `Popover` `withOverlay` prop
- Enabled container queries in the Carousel component
- Added `restrictToMarks` prop to `RangeSlider`
- Added `touchTrigger` option to `useForm` for customizing touched state logic
New Features
- use-scroll-spy hook
- TableOfContents component
- Input.ClearButton component
- Popover withOverlay prop
- Carousel container queries
- RangeSlider restrictToMarks prop
- Pagination withPages prop
- useForm touchTrigger option
Full Release Notes
[View changelog with demos on mantine.dev website](https://mantine.dev/changelog/7-16-0)
## use-scroll-spy hook
New [use-scroll-spy](https://mantine.dev/hooks/use-scroll-spy) hook tracks scroll position and returns index of the
element that is currently in the viewport. It is useful for creating
table of contents components (like in mantine.dev sidebar on the right side)
and similar features.
```tsx
import { Text, UnstyledButton } from '@mantine/core';
import { useScrollSpy } from '@mantine/hooks';
function Demo() {
const spy = useScrollSpy({
selector: '#mdx :is(h1, h2, h3, h4, h5, h6)',
});
const headings = spy.data.map((heading, index) => (
<li
key={heading.id}
style={{
listStylePosition: 'inside',
paddingInlineStart: heading.depth * 20,
background: index === spy.active ? 'var(--mantine-color-blue-light)' : undefined,
}}
>
<UnstyledButton onClick={() => heading.getNode().scrollIntoView()}>
{heading.value}
</UnstyledButton>
</li>
));
return (
<div>
<Text>Scroll to heading:</Text>
<ul style={{ margin: 0, padding: 0 }}>{headings}</ul>
</div>
);
}
```
## TableOfContents component
New [TableOfContents](https://mantine.dev/core/table-of-contents) component is built on top of `use-scroll-spy` hook
and can be used to create table of contents components like the one on the right side of mantine.dev
documentation sidebar:
```tsx
import { TableOfContents } from '@mantine/core';
function Demo() {
return (
<TableOfContents
variant="filled"
color="blue"
size="sm"
radius="sm"
scrollSpyOptions={{
selector: '#mdx :is(h1, h2, h3, h4, h5, h6)',
}}
getControlProps={({ data }) => ({
onClick: () => data.getNode().scrollIntoView(),
children: data.value,
})}
/>
);
}
```
## Input.ClearButton component
New `Input.ClearButton` component can be used to add clear button to custom inputs
based on `Input` component. `size` of the clear button is automatically
inherited from the input:
```tsx
import { Input } from '@mantine/core';
function Demo() {
const [value, setValue] = useState('clearable');
return (
<Input
placeholder="Clearable input"
value={value}
onChange={(event) => setValue(event.currentTarget.value)}
rightSection={value !== '' ? <Input.ClearButton onClick={() => setValue('')} /> : undefined}
rightSectionPointerEvents="auto"
size="sm"
/>
);
}
```
## Popover with overlay
[Popover](https://mantine.dev/core/popover) and other components based on it now support `withOverlay` prop:
```tsx
import { Anchor, Avatar, Group, Popover, Stack, Text } from '@mantine/core';
function Demo() {
return (
<Popover
width={320}
shadow="md"
withArrow
withOverlay
overlayProps={{ zIndex: 10000, blur: '8px' }}
zIndex={10001}
>
<Popover.Target>
<UnstyledButton style={{ zIndex: 10001, position: 'relative' }}>
<Avatar src="https://avatars.githubusercontent.com/u/79146003?s=200&v=4" radius="xl" />
</UnstyledButton>
</Popover.Target>
<Popover.Dropdown>
<Group>
<Avatar src="https://avatars.githubusercontent.com/u/79146003?s=200&v=4" radius="xl" />
<Stack gap={5}>
<Text size="sm" fw={700} style={{ lineHeight: 1 }}>
Mantine
</Text>
<Anchor href="https://x.com/mantinedev" c="dimmed" size="xs" style={{ lineHeight: 1 }}>
@mantinedev
</Anchor>
</Stack>
</Group>
<Text size="sm" mt="md">
Customizable React components and hooks library with focus on usability, accessibility and
developer experience
</Text>
<Group mt="md" gap="xl">
<Text size="sm">
<b>0</b> Following
</Text>
<Text size="sm">
<b>1,174</b> Followers
</Text>
</Group>
</Popover.Dropdown>
</Popover>
);
}
```
## Container queries in Carousel
You can now use [container queries](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_containment/Container_queries)
in [Carousel](https://mantine.dev/x/carousel) component. With container queries, all responsive values
are adjusted based on the container width, not the viewport width.
Example of using container queries. To see how the grid changes, resize the root element
of the demo with the resize handle located at the bottom right corner of the demo:
```tsx
import { Carousel } from '@mantine/carousel';
function Demo() {
return (
// Wrapper div is added for demonstration purposes only,
// It is not required in real projects
<div
style={{
resize: 'horizontal',
overflow: 'hidden',
maxWidth: '100%',
minWidth: 250,
padding: 10,
}}
>
<Carousel
withIndicators
height={200}
type="container"
slideSize={{ base: '100%', '300px': '50%', '500px': '33.333333%' }}
slideGap={{ base: 0, '300px': 'md', '500px': 'lg' }}
loop
align="start"
>
<Carousel.Slide>1</Carousel.Slide>
<Carousel.Slide>2</Carousel.Slide>
<Carousel.Slide>3</Carousel.Slide>
{/* ...other slides */}
</Carousel>
</div>
);
}
```
## RangeSlider restrictToMarks
[RangeSlider](https://mantine.dev/core/slider) component now supports `restrictToMarks` prop:
```tsx
import { Slider } from '@mantine/core';
function Demo() {
return (
<Stack>
<Slider
restrictToMarks
defaultValue={25}
marks={Array.from({ length: 5 }).map((_, index) => ({ value: index * 25 }))}
/>
<RangeSlider
restrictToMarks
defaultValue={[5, 15]}
marks={[
{ value: 5 },
{ value: 15 },
{ value: 25 },
{ value: 35 },
{ value: 70 },
{ value: 80 },
{ value: 90 },
]}
/>
</Stack>
);
}
```
## Pagination withPages prop
[Pagination](https://mantine.dev/core/pagination) component now supports `withPages` prop which allows hiding pages
controls and displaying only previous and next buttons:
```tsx
import { useState } from 'react';
import { Group, Pagination, Text } from '@mantine/core';
const limit = 10;
const total = 145;
const totalPages = Math.ceil(total / limit);
function Demo() {
const [page, setPage] = useState(1);
const message = `Showing ${limit * (page - 1) + 1} – ${Math.min(total, limit * page)} of ${total}`;
return (
<Group justify="flex-end">
<Text size="sm">{message}</Text>
<Pagination total={totalPages} value={page} onChange={setPage} />
</Group>
);
}
```
## useForm touchTrigger option
[use-form](https://mantine.dev/form/use-form) hook now supports `touchTrigger` option that allows customizing events that change touched state.
It accepts two options:
- `change` (default) – field will be considered touched when its value changes or it has been focused
- `focus` – field will be considered touched only when it has been focused
Example of using `focus` trigger:
```tsx
import { useForm } from '@mantine/form';
const form = useForm({
mode: 'uncontrolled',
initialValues: { a: 1 },
touchTrigger: 'focus',
});
form.isTouched('a'); // -> false
form.setFieldValue('a', 2);
form.isTouched('a'); // -> false
// onFocus is called automatically when the user focuses the field
form.getInputProps('a').onFocus();
form.isTouched('a'); // -> true
```
## Help Center updates
- [Native browser validation does not work in some components, what should I do?](https://help.mantine.dev/q/native-required) question
- [My styles are broken with disabled JavaScript. What should I do?](https://help.mantine.dev/q/disabled-js) question
- [How can I add fuzzy search to Select component?](https://help.mantine.dev/q/select-fuzzy) question
- [use-local-storage hook returns real value only after mounting, is it a bug?](https://help.mantine.dev/q/local-storage-effect) question
- [How can I upload files from Dropzone component?](https://help.mantine.dev/q/dropzone-upload) question
## Other changes
- [Autocomplete](https://mantine.dev/core/autocomplete) now supports `clearable` prop
- [where-\* mixins](https://mantine.dev/styles/postcss-preset/#where--mixins) documentation has been added
- [use-local-storage](https://mantine.dev/hooks/use-local-storage) hook now supports `sync` option which allows disabling synchronization between browser tabs