v10.9.6
Backports the following security fixes from Mermaid v11.15.0:
- CVE-2026-41150: fix(gantt): limit loop if excluding all dates (a59ea56174712ee5430dfd5bc877cb5151f501a6)
- CVE-2026-41148: fix: prevent unbalanced CSS styles in classDefs (8fead23c59166b7bab6a39eac81acebee2859102)
- CVE-2026-41149: fix: create CSS styles using the CSSOM (4e2d512bf5bf6f9de1a8f0a48da78dc4d09ac4f3)
- CVE-2026-41159: fix: block stylis scope escape (a9d9f0d8eb790349121508688cd338253fd80d76)
- CVE-2026-41907: fix: loosen
uuiddependency range to allow v14 We don't use this vulnerable code, but it allows users to silence this warning. (e8c70434520dd9226bd442f857970cd211067627)
And other dependency updates.
Full Changelog: https://github.com/mermaid-js/mermaid/compare/v10.9.5...v10.9.6
9.2.0 🔥
View changelog with demos on mantine.dev website
You can now sponsor Mantine development with OpenCollective. All funds are used to improve Mantine and create new features and components.
New TreeSelect component allows picking one or more values from hierarchical tree data. It supports three selection modes: single, multiple, and checkbox (with parent-child cascade):
import { TreeSelect } from '@mantine/core';
import { data } from './data';
function Demo() {
return (
<TreeSelect
label="Your favorite item"
placeholder="Pick value"
data={data}
/>
);
}
New Combobox examples showing how to build tree select components from Combobox primitives with connecting lines, expand/collapse chevrons, and proper indentation:
- Tree select – basic single-value tree select
- Tree multi select – multi select with checkbox cascade
- Searchable tree select – tree select with search filtering
- Tree select with checkboxes – single select with expand-on-click
- Virtualized tree select – large tree (~500 nodes) with @tanstack/react-virtual
@mantine/notifications now supports dismissing notifications by dragging them left or right, and with horizontal scroll swipe while hovered. Both interactions can be disabled on Notifications, and individual items can opt out with allowClose: false.
import { Button } from '@mantine/core';
import { notifications } from '@mantine/notifications';
function Demo() {
return (
<Button
onClick={() =>
notifications.show({
title: 'Default notification',
message: 'Do not forget to star Mantine on GitHub! 🌟',
})
}
>
Show notification
</Button>
);
}
New use-drag hook handles pointer drag gestures with movement tracking, velocity, direction and axis constraints. It uses the Pointer Events API and works with both mouse and touch input:
import { useState } from 'react';
import { Button, Group, Paper, Text } from '@mantine/core';
import { useDrag } from '@mantine/hooks';
interface NotificationItem {
id: number;
text: string;
}
function SwipeNotification({
notification,
onDismiss,
}: {
notification: NotificationItem;
onDismiss: (id: number) => void;
}) {
const [offset, setOffset] = useState(0);
const [dismissed, setDismissed] = useState(false);
const { ref, active } = useDrag(
(state) => {
if (state.last) {
const shouldDismiss =
Math.abs(state.movement[0]) > 120 || state.velocity[0] > 0.5;
if (shouldDismiss) {
setDismissed(true);
setTimeout(() => onDismiss(notification.id), 300);
} else {
setOffset(0);
}
} else {
setOffset(state.movement[0]);
}
},
{ axis: 'x', threshold: 5, filterTaps: true }
);
return (
<Paper
ref={ref}
p="sm"
mb="xs"
withBorder
radius="md"
style={{
transform: dismissed
? `translateX(${offset > 0 ? 400 : -400}px)`
: `translateX(${offset}px)`,
opacity: dismissed ? 0 : 1 - Math.min(Math.abs(offset) / 200, 1) * 0.6,
transition: active ? 'none' : 'transform 300ms ease, opacity 300ms ease',
cursor: active ? 'grabbing' : 'grab',
touchAction: 'pan-y',
userSelect: 'none',
}}
>
{notification.text}
</Paper>
);
}
const initialItems: NotificationItem[] = [
{ id: 1, text: 'New message from Alice' },
{ id: 2, text: 'Build succeeded' },
{ id: 3, text: 'Deployment complete' },
{ id: 4, text: 'Review requested' },
];
function Demo() {
const [notifications, setNotifications] = useState(initialItems);
return (
<div style={{ height: 300 }}>
{notifications.map((n) => (
<SwipeNotification
key={n.id}
notification={n}
onDismiss={(id) =>
setNotifications((items) => items.filter((item) => item.id !== id))
}
/>
))}
{notifications.length === 0 && (
<Text ta="center" c="dimmed" py="md">All cleared!</Text>
)}
<Group justify="center" mt="md">
<Button onClick={() => setNotifications(initialItems)}>
Reset
</Button>
</Group>
</div>
);
}
New InlineDateTimePicker component renders a calendar with a time picker inline, without a dropdown. It supports both default and range modes:
import { InlineDateTimePicker } from '@mantine/dates';
function Demo() {
return <InlineDateTimePicker />;
}
Set type="range" to select a date and time range with two time inputs:
import { InlineDateTimePicker } from '@mantine/dates';
function Demo() {
return <InlineDateTimePicker type="range" />;
}
DateTimePicker now supports type="range" to select a date and time range. In range mode, two time inputs are displayed in the dropdown for start and end times:
import { DateTimePicker } from '@mantine/dates';
function Demo() {
return (
<DateTimePicker
type="range"
label="Pick dates and times range"
placeholder="Pick dates and times range"
/>
);
}
DateTimePicker valueFormat prop now accepts a function in addition to a dayjs format string. The callback receives the value as a YYYY-MM-DD HH:mm:ss string and returns the formatted value, which is useful for cases that cannot be expressed with a dayjs format string:
import dayjs from 'dayjs';
import { DateTimePicker } from '@mantine/dates';
function Demo() {
return (
<DateTimePicker
valueFormat={(date) => dayjs(date).format('dddd, MMMM D [at] h:mm A')}
defaultValue="2024-04-11 14:45:00"
label="Pick date and time"
placeholder="Pick date and time"
/>
);
}
New RollingNumber component animates value changes with rolling digit transitions. Each digit independently rolls to its new position when the value changes:
import { useState } from 'react';
import { Button, Group, RollingNumber } from '@mantine/core';
function Demo() {
const [value, setValue] = useState(1234);
return (
<>
<RollingNumber value={value} fz="36px" />
<Group mt="md">
<Button onClick={() => setValue((v) => v + 1)}>Increment</Button>
<Button onClick={() => setValue((v) => v - 1)}>Decrement</Button>
<Button onClick={() => setValue(Math.floor(Math.random() * 10000))}>Random</Button>
</Group>
</>
);
}
MaskInput now supports a resetRef prop that assigns a function that clears the input value imperatively. This is useful because MaskInput is uncontrolled internally, so setting value from a parent does not clear it:
import { useRef } from 'react';
import { MaskInput, Button, Group } from '@mantine/core';
function Demo() {
const resetRef = useRef<() => void>(null);
return (
<>
<MaskInput
label="Phone number"
placeholder="(___) ___-____"
mask="(999) 999-9999"
resetRef={resetRef}
/>
<Group mt="md">
<Button onClick={() => resetRef.current?.()}>Reset</Button>
</Group>
</>
);
}
MaskInput integration with use-form is now documented. Use defaultValue to seed the initial value and onChangeRaw to write the raw value to form state:
import { Button, MaskInput } from '@mantine/core';
import { useForm } from '@mantine/form';
function Demo() {
const form = useForm({
mode: 'uncontrolled',
initialValues: { phone: '' },
});
return (
<form onSubmit={form.onSubmit((values) => console.log(values))}>
<MaskInput
mask="(999) 999-9999"
placeholder="(___) ___-____"
label="Phone"
onChangeRaw={(raw) => form.setFieldValue('phone', raw)}
/>
<Button type="submit" mt="md">
Submit
</Button>
</form>
);
}
New SankeyChart component visualizes flow between nodes as a Sankey diagram where the width of each link is proportional to the flow value:
// Demo.tsx
import { SankeyChart } from '@mantine/charts';
import { data } from './data';
function Demo() {
return <SankeyChart data={data} />;
}
// data.ts
export const data = {
nodes: [
{ name: 'Visit' },
{ name: 'Direct-Favourite' },
{ name: 'Page-Click' },
{ name: 'Detail-Favourite' },
{ name: 'Lost' },
],
links: [
{ source: 0, target: 1, value: 3728.3 },
{ source: 0, target: 2, value: 354170 },
{ source: 2, target: 3, value: 62429 },
{ source: 2, target: 4, value: 291741 },
],
};
MultiSelect and TagsInput now support reordering selected pills. Set the new withPillsReorder prop to enable it. Pills can be reordered with a mouse (drag-and-drop) or keyboard:
- Pills are not part of the
Taborder.ArrowLeftfrom the input (caret at start) moves focus to the last pill. ArrowLeftandArrowRightnavigate between pills (RTL-aware).ArrowRighton the last pill returns focus to the input.Alt + ArrowLeftandAlt + ArrowRightreorder the focused pill (RTL-aware). Focus follows the moved pill so chained moves work.
Reordering is automatically disabled when disabled or readOnly is set. Custom pill renderers receive a reorderProps payload that can be spread onto the pill element to keep reordering working:
import { useState } from 'react';
import { MultiSelect } from '@mantine/core';
function Demo() {
const [value, setValue] = useState(['React', 'Angular', 'Vue']);
return (
<MultiSelect
label="Drag pills to reorder"
description="Selected values can be reordered by dragging pills"
placeholder="Pick value"
data={['React', 'Angular', 'Vue', 'Svelte', 'Solid', 'Ember']}
value={value}
onChange={setValue}
withPillsReorder
/>
);
}
Tree component now supports restricting drop targets with the new allowDrop prop. The callback receives { draggedNode, targetNode, position } and returning false hides the drop indicator and rejects the drop, so users get proper visual feedback before releasing:
import { useState } from 'react';
import { CaretDownIcon } from '@phosphor-icons/react';
import { Group, moveTreeNode, RenderTreeNodePayload, Tree, TreeNodeData } from '@mantine/core';
const data: TreeNodeData[] = [
{
label: 'Pages',
value: 'pages',
children: [
{ label: 'index.tsx', value: 'pages/index.tsx' },
{ label: 'about.tsx', value: 'pages/about.tsx' },
],
},
{
label: 'Components (locked)',
value: 'components',
children: [
{ label: 'Header.tsx', value: 'components/Header.tsx' },
{ label: 'Footer.tsx', value: 'components/Footer.tsx' },
],
},
{ label: 'package.json', value: 'package.json' },
];
function Leaf({ node, expanded, hasChildren, elementProps }: RenderTreeNodePayload) {
return (
<Group gap={5} {...elementProps}>
{hasChildren && (
<CaretDownIcon
size={18}
style={{ transform: expanded ? 'rotate(180deg)' : 'rotate(0deg)' }}
/>
)}
<span>{node.label}</span>
</Group>
);
}
function Demo() {
const [treeData, setTreeData] = useState(data);
return (
<Tree
data={treeData}
// Forbid dropping into or onto "components" branch
allowDrop={({ draggedNode, targetNode, position }) => {
if (draggedNode === 'components' || draggedNode.startsWith('components/')) {
return false;
}
if (targetNode === 'components' && position === 'inside') {
return false;
}
return !targetNode.startsWith('components/');
}}
onDragDrop={(payload) =>
setTreeData((current) => moveTreeNode(current, payload))
}
renderNode={(payload) => <Leaf {...payload} />}
/>
);
}
Tree component now supports restricting drag initiation to a dedicated handle with the new withDragHandle prop. The handle spreads dragHandleProps from the renderNode payload. This is useful when a node contains interactive controls (inputs, buttons) that would otherwise interfere with dragging:
import { useState } from 'react';
import { CaretDownIcon, DotsSixVerticalIcon } from '@phosphor-icons/react';
import { Group, moveTreeNode, RenderTreeNodePayload, Tree, TreeNodeData } from '@mantine/core';
const data: TreeNodeData[] = [
{
label: 'Pages',
value: 'pages',
children: [
{ label: 'index.tsx', value: 'pages/index.tsx' },
{ label: 'about.tsx', value: 'pages/about.tsx' },
],
},
{
label: 'Components',
value: 'components',
children: [
{ label: 'Header.tsx', value: 'components/Header.tsx' },
{ label: 'Footer.tsx', value: 'components/Footer.tsx' },
],
},
{ label: 'package.json', value: 'package.json' },
];
function Leaf({ node, expanded, hasChildren, elementProps, dragHandleProps }: RenderTreeNodePayload) {
return (
<Group gap={4} {...elementProps}>
<DotsSixVerticalIcon
{...dragHandleProps}
size={16}
style={{ cursor: 'grab' }}
/>
{hasChildren && (
<CaretDownIcon
size={18}
style={{ transform: expanded ? 'rotate(180deg)' : 'rotate(0deg)' }}
/>
)}
<span>{node.label}</span>
</Group>
);
}
function Demo() {
const [treeData, setTreeData] = useState(data);
return (
<Tree
data={treeData}
withDragHandle
onDragDrop={(payload) =>
setTreeData((current) => moveTreeNode(current, payload))
}
renderNode={(payload) => <Leaf {...payload} />}
/>
);
}
Default props set on Input and Input.Wrapper in theme.components now cascade to every component built on top of them (TextInput, Textarea, NumberInput, Select, DateInput, and others). This makes it possible to apply shared size, radius, variant, withAsterisk and other props to all inputs at once, while still overriding individual components with their own default props:
import { TextInput, NumberInput, NativeSelect, MantineProvider, createTheme, Input } from '@mantine/core';
const theme = createTheme({
components: {
Input: Input.extend({
defaultProps: {
size: 'md',
radius: 'md',
},
}),
InputWrapper: Input.Wrapper.extend({
defaultProps: {
withAsterisk: true,
},
}),
NumberInput: NumberInput.extend({
defaultProps: {
size: 'lg',
},
}),
},
});
function Demo() {
return (
<MantineProvider theme={theme}>
<TextInput label="Text input" placeholder="Inherits size and radius from Input" />
<NativeSelect
mt="md"
label="Native select"
data={['React', 'Angular', 'Vue', 'Svelte']}
/>
<NumberInput mt="md" label="Number input" placeholder="Overrides shared size with lg" />
</MantineProvider>
);
}
WeekView businessHours prop now accepts a per-day object keyed by day of the week (0 – Sunday, 6 – Saturday) in addition to the shared [start, end] tuple. Days missing from the object or set to null are rendered as fully outside business hours, making it easy to model partial workdays and non-working days:
import { WeekView } from '@mantine/schedule';
import { events } from './data';
function Demo() {
return (
<WeekView
date={new Date()}
events={events}
highlightBusinessHours
businessHours={{
1: ['09:00:00', '17:00:00'],
2: ['09:00:00', '17:00:00'],
3: ['09:00:00', '17:00:00'],
4: ['09:00:00', '17:00:00'],
5: ['09:00:00', '13:00:00'],
}}
startTime="07:00:00"
endTime="20:00:00"
/>
);
}
@mermaid-js/mermaid-zenuml@0.2.3
-
#7647
45a9498Thanks @MrCoder! - fix: update @zenuml/core to v3.46.11 with native SVG renderer- Fix vertical lifelines disappearing when printing (#6004)
- Fix SVG dimensions exceeding container boundaries (#7266)
- Fix invalid ZenUML syntax freezing the editor (#7154)
-
Updated dependencies [
e9b0f34,37ff937,bfe60cc,faafb5d,65f8be2,50b2166,8e17492,9ad8dde,27db774,bf9502f,1502f32,1f98db8,2343e38,7fb9509,3f9e0f1,7a8fb85,5144ed4,13d9bfa,e14bb88,9217c0d,5e7eb62,afaf306,4755553,88cdd3d,6476973,8c1a0c1]:- mermaid@11.15.0
mermaid@11.15.0
-
#7174
0aca217Thanks @milesspencer35! - feat(sequence): Add support for decimal start and increment values in theautonumberdirective -
#7512
8e17492Thanks @aruncveli! - feat(flowchart): add datastore shapeIn Data flow diagrams, a datastore/warehouse/file/database is used to represent data persistence. It is denoted by a rectangle with only top and bottom borders, and can be used in flowcharts with
A@{ shape: datastore, label: "Datastore" }. -
#6440
9ad8ddeThanks @yordis, @lgazo! - feat: add Event Modeling diagram -
#7707
27db774Thanks @txmxthy! - feat(architecture): expose four fcose layout knobs forarchitecture-betadiagrams (nodeSeparation,idealEdgeLengthMultiplier,edgeElasticity,numIter) so authors can tune layout density and spread overlapping siblings without changing diagram source -
#7604
bf9502fThanks @M-a-c! - feat(class): add nested namespace support for class diagrams via dot notation and syntactic nestingIf you have namespaces in class diagrams that use
.s already and want to render them without nesting (≤v11.14.0 behaviour), you can use setclass.hierarchicalNamespaces=falsein your mermaid config:config: class: hierarchicalNamespaces: false
-
#7272
88cdd3dThanks @xinbenlv! - feat(sankey): add outlined label style, configurable nodeWidth/nodePadding, and custom node colors
-
#7737
e9b0f34Thanks @ashishjain0512! - fix: prevent unbalanced CSS styles in classDefs -
#7737
37ff937Thanks @ashishjain0512! - fix: create CSS styles using the CSSOMThis removes some invalid CSS and normalizes some CSS formatting.
-
#7508
bfe60ccThanks @biiab! - fix(stateDiagram):end notenow only closes a note when used on a new line -
#7737
faafb5dThanks @ashishjain0512! - fix(gantt): add iteration limit forexcludesfield -
#7737
65f8be2Thanks @ashishjain0512! - fix: disallow some CSS at-rules in custom CSS -
#7726
1502f32Thanks @aloisklink! - fix(wardley): fix unnecessary sanitization of text -
#7578
1f98db8Thanks @Gaston202! - fix(class): self-referential class multiplicity labels no longer rendered multiple timesFixes #7560. Resolves an issue where cardinality labels on self-referential class relationships were rendered three times due to edge splitting in the dagre layout. The fix ensures that each sub-edge only carries its relevant label positions.
-
#7592
2343e38Thanks @knsv-bot! - fix(sequence): add background box behind alt/else section title labels in sequence diagrams -
#7589
7fb9509Thanks @NYCU-Chung! - fix(block): prevent column widths from shrinking when mixing different column spans -
#7632
3f9e0f1Thanks @ekiauhce! - fix(sequence): correct messageAlign label position for right-to-left arrows in sequence diagrams -
#7642
7a8fb85Thanks @tractorjuice! - fix(wardley): allow hyphens in unquoted component namesMulti-word names containing hyphens — e.g.
real-time processing,end-user,on-call engineer— now parse without quoting, bringing the grammar in line with the OnlineWardleyMaps (OWM) convention.A->B(no-space arrow) still tokenises correctly. -
#7523
5144ed4Thanks @darshanr0107! - fix(block): Arrow blocks in block-beta diagrams not spanning the specified number of columns when using:nsyntax. -
#7262
13d9bfaThanks @darshanr0107! - fix(block): Ensure block diagram hexagon blocks respect column spanning syntax -
#7684
e14bb88Thanks @aloisklink! - fix: loosenuuiddependency range to allow v14Mermaid does not use any of the vulnerable code in CVE-2026-41907, but this allows users to silence any
npm auditalerts on it. -
#7633
9217c0dThanks @Felix-Garci! - fix(block): add support for all arrow types in block diagrams -
#7587
5e7eb62Thanks @MaddyGuthridge! - chore: drop lodash-es in favour of es-toolkit -
#7693
afaf306Thanks @dull-bird! - fix(quadrant-chart): allow CJK, emoji, Latin-1 accented characters, and other non-ASCII text in unquoted axis/quadrant/point labels.Previously the lexer only matched ASCII
[A-Za-z]+for text tokens, even though the grammar referencedUNICODE_TEXT. Bare Chinese, Japanese, Korean, emoji, and accented Latin characters in labels caused a parse error. Added a[^\x00-\x7F]+lexer rule to emitUNICODE_TEXTand included it in thealphaNumTokengrammar rule.Fixes #7120.
-
#7737
4755553Thanks @ashishjain0512! - fix: improve D3 types for mermaidAPI funcs -
#7737
6476973Thanks @ashishjain0512! - fix: handle&when namespacing CSS rules -
#7520
8c1a0c1Thanks @RodrigojndSantos! - fix(stateDiagram): comments starting with one%are no longer treated as commentsSwitch to using two
%%if you want to write a comment. -
Updated dependencies [
7a8fb85,675a64c]:- @mermaid-js/parser@1.1.1
@mermaid-js/parser@1.1.1
-
#7642
7a8fb85Thanks @tractorjuice! - fix(wardley): allow hyphens in unquoted component namesMulti-word names containing hyphens — e.g.
real-time processing,end-user,on-call engineer— now parse without quoting, bringing the grammar in line with the OnlineWardleyMaps (OWM) convention.A->B(no-space arrow) still tokenises correctly. -
#7658
675a64cThanks @aloisklink! - fix(parser): bundle langium/chevrotainThis should silence warnings about lodash-es 4.17.23, which chevrotain@11.1.1 is pinned to, but is not vulnerable to.
And this avoids warnings when langium v4 is installed on Node.JS v20.0.
Full Changelog: https://github.com/mermaid-js/mermaid/blob/41646dfd43ac83f001b03c70605feb036afae46d/packages/parser/CHANGELOG.md