@gfazioli/mantine-window
A Mantine extension component that renders draggable, resizable floating windows with persistent state, customizable boundaries, collapsible content, z-index management, and flexible control over position, size, and interaction modes. Includes a WindowGroup compound component for coordinated multi-window management with layout presets.
Upgrading from an older major version? See the Upgrade guide for breaking changes and step-by-step migration instructions.
Installation
After installation import package styles at the root of your application:
You can import styles within a layer @layer mantine-window by importing @gfazioli/mantine-window/styles.layer.css file.
Usage
The Window component allows you to create draggable and resizable windows within your application. It provides a familiar desktop-like experience for users. You can customize the appearance and behavior of the windows using various props.
Note:
Usually, you would want to use the
Windowcomponent within a portal to avoid layout issues. However, if you want the window to be constrained within a specific container, you can set thewithinPortalprop tofalseand the container should haveposition: relativestyle.For example, in the demos below, the
Windowcomponent is rendered relative to its parent container which hasposition: relativestyle.
Hello, World! Hello, Mantiners! Say Hello to the Window component
Controlled
You can control the open and close state of the Window component using the opened and onClose props. This allows you to manage the visibility of the window programmatically.
Position and Size
Set the initial position and size of the window using defaultX, defaultY, defaultWidth, and defaultHeight props. These values are used when the window is first rendered and can be modified by the user via drag and resize.
Custom Position & Size
Controlled Position and Size
For full external control, use the x, y, width, and height props. When these are set, the component does not manage that value internally — your state drives it. Combine with onPositionChange and onSizeChange to sync state after user interactions.
You can also mix controlled and uncontrolled: for example, control only x and y while letting width and height remain uncontrolled via defaultWidth and defaultHeight.
X: 50
Y: 50
Width: 300
Controlled
Within Portal vs Container
The withinPortal prop controls how the window is positioned. When withinPortal={true} (default), the window uses fixed positioning relative to the viewport. When withinPortal={false}, it uses absolute positioning relative to its parent container.
Fixed positioning (withinPortal=true):
- Window stays in place when scrolling
- Positioned relative to the browser viewport
- Best for modals and overlays
Absolute positioning (withinPortal=false):
- Window is constrained within parent container
- Parent must have
position: relative - Window cannot be dragged or resized outside container boundaries
- Best for embedded UI components
Container with position: relative
Unit Types
Position and size props support three unit types:
- Pixels (number): Fixed pixel values, e.g.
defaultX={100} - Viewport units (string):
vwfor viewport width,vhfor viewport height, e.g.defaultX="10vw" - Percentages (string): Relative to the reference container, e.g.
defaultX="20%"
Understanding Unit References:
vw/vh: Always relative to the browser viewport, regardless ofwithinPortalsetting%: WithwithinPortal={true}(default) relative to the viewport; withwithinPortal={false}relative to the parent container- Pixels: Always fixed, regardless of
withinPortalsetting
You can mix different unit types freely on the same component.
Pixels
Percentages
Mixed
Responsive
All dimension and position props support responsive values using Mantine breakpoints. Pass an object with breakpoint keys (base, xs, sm, md, lg, xl) to change values at different screen sizes.
This also applies to minWidth, maxWidth, minHeight, maxHeight, radius, and shadow.
Responsive Window
Min/Max Size Constraints
Control the minimum and maximum dimensions during resize operations using minWidth, minHeight, maxWidth, and maxHeight props. These support all unit types (pixels, viewport units, percentages) and responsive breakpoint objects.
Constrained Resize
You can mix different unit types for constraints — for example, a fixed pixel minimum with a viewport-based maximum:
Drag Boundaries
Restrict the draggable area of the window using the dragBounds prop. This ensures the window stays within specific boundaries. The dragBounds object accepts minX, maxX, minY, and maxY values, each supporting pixels, viewport units, and percentages.
Bounded Dragging
Combine different unit types (pixels, viewport units, percentages) for drag boundaries to create flexible constraints:
Centered Window
Create a centered, fixed window that cannot be moved or resized by combining position/size props with resizable="none" and draggable="none".
Centered Window
Disable Collapsing
Prevent the window from being collapsed by setting collapsable={false}. This removes the collapse functionality, including the double-click handler on the header.
No Collapsable
Disable ScrollArea
By default, the window content is wrapped in a ScrollArea component. Set withScrollArea={false} to disable it — content that overflows will be clipped instead of scrollable. This is useful when your content already provides its own scrolling mechanism or when the ScrollArea interferes with the inner layout.
With ScrollArea (default)
Without ScrollArea
Controls Position
The window title bar buttons (close, collapse, tools) default to the left side (macOS style). Set controlsPosition="right" to move them to the right side (Windows style). The button order is automatically reversed when positioned on the right — close moves to the far right, matching platform conventions.
For full control over button ordering, use the controlsOrder prop with an array of 'close', 'collapse', and 'tools' in your desired sequence. When controlsOrder is set, it overrides the automatic ordering.
macOS style (default)
Windows style
Custom order
Full Size Resize Handles
By default, resize handles (top, bottom, left, right) are centered and have a limited size (40px). Setting fullSizeResizeHandles={true} makes these handles span the entire width or height of the window edge, providing a larger interaction area.
Full Size Resize Handles
Persistence
Enable state persistence to save the window's position and size in localStorage by setting persistState={true} (default is false). When enabled, the window will remember its position and size across page refreshes.
With Persistence
Callbacks
Use onPositionChange and onSizeChange callbacks to respond to window movements and resize operations. These callbacks receive the current position or size as pixel values.
With Callbacks
Multiple Windows
You can render multiple windows in the same container. Each window maintains its own z-index and can be brought to the front by clicking on it. Use unique id props to ensure proper state persistence.
Window 1
Window 2
Context Menu
Mantine 9.3 introduces the Menu.ContextMenu component: attach desktop-style right-click actions to any surface. Inside a Window it completes the desktop metaphor — and in this demo the actions are real: duplicate the window or close it, with the windows rendered dynamically from state.
Window 1
Window Menu Bar
The Menu.Search, Menu.CheckboxItem and Menu.RadioGroup components added in Mantine 9.3 make a compact in-window menu bar straightforward. In this demo every menu entry drives the workspace for real: the searchable File menu reports the executed command in the status bar, while the View menu toggles the canvas grid and changes the layout density.
Design Workspace
Window.Group
Wrap multiple windows in a Window.Group to enable coordinated window management. The group provides:
- Coordinated z-index: Windows within a group share a stacking context
- Layout presets: Snap, tile, and fill layouts via the green tools button in each window header
- Global actions: Collapse all, expand all, close all
Each Window inside a Window.Group must have a unique id prop. The group defaults to withinPortal={false} (overridable via the withinPortal prop) and applies position: relative on its container.
Editor
Preview
Console
Tools Button
The green tools button in the window header provides layout options. It is controlled by the withToolsButton prop (default: true) and is available on every window, even outside a Window.Group.
Without a Group, the menu shows only single-window actions:
- Move & Resize: Snap left, snap right, snap top, snap bottom — positions the current window to fill half of the container or viewport
- Fill: Maximizes the current window to fill the entire available area
Inside a Group, the menu also shows group-wide actions:
- Arrange Columns / Rows: Distributes all visible windows in equal columns or rows
- Tile: Arranges all visible windows in an automatic grid
- Collapse all / Expand all: Collapses or expands all windows that have
collapsable={true}. Windows withcollapsable={false}are skipped. - Close all: Closes all windows in the group
The onLayoutChange callback on Window.Group notifies you when a group layout is applied.
Last layout applied:
Window 1
Window 2
Window 3
Window 4
Layout Presets
Use defaultLayout to apply an initial layout when the group mounts. To control the layout externally, use the groupRef prop to access the group API and call applyLayout programmatically.
Window 1
Window 2
Window 3
Dynamic Windows
Windows inside a Window.Group can be rendered dynamically from an array — for example via .map() over a piece of state. Layout presets and group actions wait until the registry is populated and the container is measured, so a layout triggered before all children have mounted is automatically deferred and flushed when everything is ready.
Window alpha
Window beta
Z-index Management
Both Window.Group and stand-alone Window expose z-index configuration via three props:
initialZIndex— starting value for the stacking context. Defaults to200whenwithinPortalistrue,1otherwise.maxZIndex— upper bound. When the counter would exceed this value it wraps back toinitialZIndex, preventing unbounded escalation above modals or menus.zIndexStrategy(Window.Grouponly) — how z-indexes are assigned when a window is focused or registered:'increment'(default): a counter monotonically increases. Preserves legacy behavior. Combine withmaxZIndexto keep values bounded.'normalize': z-indexes are derived from the stacking order and always stay withininitialZIndex .. initialZIndex + N - 1. Recommended for long-running applications or when Mantine modals and menus are used alongside.
Click each window in any order. With normalize, z-indexes always stay compact ({100..102}). With increment, the counter keeps growing unless maxZIndex is set.
Alpha
Beta
Gamma
Styles API
Window supports Styles API, you can add styles to any inner element of the component with classNames prop. Follow Styles API documentation to learn more.
Styles API
Component Styles API
Hover over selectors to highlight corresponding elements