Form context

@mantine/form package now exports createFormContext function to create provider component, hook to get form object from context and use-form hook with predefined type:

import { createFormContext } from '@mantine/form';
import { TextInput } from '@mantine/core';
// Definition of form values is required
interface FormValues {
age: number;
name: string;
}
// createFormContext returns a tuple with 3 items:
// FormProvider is a component that sets form context
// useFormContext hook return form object that was previously set in FormProvider
// useForm hook works the same way as useForm exported from the package but has predefined type
const [FormProvider, useFormContext, useForm] = createFormContext<FormValues>();
function ContextField() {
const form = useFormContext();
return <TextInput label="Your name" {...form.getInputProps('name')} />;
}
export function Context() {
// Create form as described in use-form documentation
const form = useForm({
initialValues: {
age: 0,
name: '',
},
});
// Wrap your form with FormProvider
return (
<FormProvider form={form}>
<form onSubmit={form.onSubmit(() => {})}>
<ContextField />
</form>
</FormProvider>
);
}

Indicator improvements

Indicator component now includes more features to work with number labels and processing prop:

999
999
999
import { useState } from 'react';
import { Avatar, Indicator, Button, Group } from '@mantine/core';
function Demo() {
const [count, setCount] = useState(9);
return (
<>
<Group position="center" spacing="xl">
<Indicator label={count} inline size={22}>
<Avatar size="lg" src="avatar.png" />
</Indicator>
<Indicator label={count} overflowCount={10} inline size={22}>
<Avatar size="lg" src="avatar.png" />
</Indicator>
<Indicator label={count} showZero={false} dot={false} overflowCount={999} inline size={22}>
<Avatar size="lg" src="avatar.png" />
</Indicator>
</Group>
<Group position="center" mt="xl">
<Button variant="outline" onClick={() => setCount((old) => (old > 0 ? old - 1 : old))}>
Decrement
</Button>
<Button variant="outline" onClick={() => setCount((old) => old + 1)}>
Increment
</Button>
</Group>
</>
);
}
import { Avatar, Indicator } from '@mantine/core';
function Demo() {
return (
<Indicator inline dot processing size={12}>
<Avatar size="lg" src="avatar.png" />
</Indicator>
);
}

Switch improvements

Switch component now supports thumbIcon prop and any React node can now be used on onLabel and offLabel props:

import { Switch, Group, useMantineTheme } from '@mantine/core';
import { IconSun, IconMoonStars } from '@tabler/icons';
function Demo() {
const theme = useMantineTheme();
return (
<Group position="center">
<Switch
size="md"
color={theme.colorScheme === 'dark' ? 'gray' : 'dark'}
onLabel={<IconSun size={16} stroke={2.5} color={theme.colors.yellow[4]} />}
offLabel={<IconMoonStars size={16} stroke={2.5} color={theme.colors.blue[6]} />}
/>
</Group>
);
}
import { useState } from 'react';
import { Switch, Group, useMantineTheme } from '@mantine/core';
import { IconCheck, IconX } from '@tabler/icons';
function Demo() {
const theme = useMantineTheme();
const [checked, setChecked] = useState(false);
return (
<Group position="center">
<Switch
checked={checked}
onChange={(event) => setChecked(event.currentTarget.checked)}
color="teal"
size="md"
label="Switch with thumb icon"
thumbIcon={
checked ? (
<IconCheck size={12} color={theme.colors.teal[theme.fn.primaryShade()]} stroke={3} />
) : (
<IconX size={12} color={theme.colors.red[theme.fn.primaryShade()]} stroke={3} />
)
}
/>
</Group>
);
}

Grid improvements

Grid.Col component now supports setting column span (and other related responsive props) to auto and content:

1
2
3
import { Grid } from '@mantine/core';
function Demo() {
return (
<Grid>
<Grid.Col span="auto">span=auto</Grid.Col>
<Grid.Col span={6}>span=6</Grid.Col>
<Grid.Col span="auto">span=auto</Grid.Col>
</Grid>
);
}
fit content
2
import { Grid } from '@mantine/core';
function Demo() {
return (
<Grid>
<Col span="content">fit content</Col>
<Col span={6}>2</Col>
</Grid>
);
}

use-previous hook

use-previous hook stores the previous value of a state in a ref, it returns undefined on initial render and the previous value of a state after rerender:

Current value:
Previous value:
import { TextInput, Text } from '@mantine/core';
import { usePrevious, useInputState } from '@mantine/hooks';
function Demo() {
const [value, setValue] = useInputState('');
const previousValue = usePrevious(value);
return (
<div>
<TextInput
label="Enter some text here"
placeholder="Enter some text here"
id="previous-demo-input"
value={value}
onChange={setValue}
/>
<Text mt="md">Current value: {value}</Text>
<Text>Previous value: {previousValue}</Text>
</div>
);
}

Other changes