Mantine Window

Undolog

@gfazioli/mantine-window

A Mantine extension component that renders interactive JSON trees with syntax highlighting, collapsible nodes, copy-to-clipboard, and configurable expansion depth.

Installation

yarn add @gfazioli/mantine-window

After installation import package styles at the root of your application:

import '@gfazioli/mantine-window/styles.css';

You can import styles within a layer @layer mantine-window by importing @gfazioli/mantine-window/styles.layer.css file.

import '@gfazioli/mantine-window/styles.layer.css';

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. Also, you can choose whether the window should be rendered within a portal or relative to its parent container using the withinPortal prop.

Note:

Usually, you would want to use the Window component within a portal to avoid layout issues. However, if you want the window to be constrained within a specific container, you can set the withinPortal prop to false and the container should have position: relative style.

For example, in the demos below, the Window component is rendered relative to its parent container which has position: relative style.

Hello, World! Hello, Mantiners! Say Hello to the Window component

This is a window with data

Color
Shadow
Radius
import { Window, type WindowProps } from '@gfazioli/mantine-window';
import { Box, Title } from '@mantine/core';
function Demo() {
  return (
    {/** In this demo, the Window is positioned relative to its parent container. Check the docs below to learn more about the "withinPortal" prop. */}
    <Box pos="relative" style={{ width: '100%', height: 500 }}>
      <Window
        defaultPosition={{ x: 0, y: 0}}
        defaultSize={{ width: 320, height: 256 }}
        persistState={false}
        withinPortal={false}
        maxWidth={500}
        maxHeight={500}
        opened
        title="Hello, World! Hello, Mantiners! Say Hello to the Window component"
      >
        <Title order={4}>This is a window with data</Title>
      </Window>
    </Box>
  );
}

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.

import { Window } from '@gfazioli/mantine-window';
import { Button, Stack, Title } from '@mantine/core';
import { useDisclosure } from '@mantine/hooks';

function Demo() {
  const [opened, { open, close }] = useDisclosure(false);

  return (
    <Stack pos="relative" style={{ width: '100%', height: 500 }}>
      <Button onClick={open}>Open window</Button>
      <Window title="Hello World" opened={opened} onClose={close} withinPortal={false}>
        <Title order={4}>This is a window</Title>
      </Window>
    </Stack>
  );
}

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

import { Window } from '@gfazioli/mantine-window';
import { Button, Group, Stack, Text, Title } from '@mantine/core';
import { useDisclosure } from '@mantine/hooks';

function Demo() {
  const [portalOpened, { open: openPortal, close: closePortal }] = useDisclosure(false);
  const [containerOpened, { open: openContainer, close: closeContainer }] = useDisclosure(false);

  return (
    <>
      <Group mb="md">
        <Button onClick={openPortal}>Open in Portal (Fixed)</Button>
        <Button onClick={openContainer}>Open in Container (Relative)</Button>
      </Group>

      {/* Window with withinPortal={true} (default) - fixed positioning */}
      <Window 
        title="Fixed Window (Portal)" 
        opened={portalOpened} 
        onClose={closePortal}
        withinPortal={true}
        persistState={false}
        defaultPosition={{ x: 100, y: 100 }}
      >
        <Stack p="md">
          <Title order={4}>Portal Window</Title>
          <Text size="sm">
            This window uses <strong>withinPortal=true</strong> (default).
            It is positioned fixed relative to the viewport and will stay in place even when scrolling.
          </Text>
        </Stack>
      </Window>

      {/* Container for relative window */}
      <Stack pos="relative" style={{ width: '100%', height: 400, border: '2px dashed gray' }} p="md">
        <Text c="dimmed" size="sm">Container with position: relative</Text>
        
        {/* Window with withinPortal={false} - absolute positioning */}
        <Window 
          title="Relative Window (Container)" 
          opened={containerOpened} 
          onClose={closeContainer}
          withinPortal={false}
          persistState={false}
          defaultPosition={{ x: 0, y: 0 }}
          defaultSize={{ width: 400, height: 300 }}
        >
          <Stack p="md">
            <Title order={4}>Container Window</Title>
            <Text size="sm">
              This window uses <strong>withinPortal=false</strong>.
              It is positioned absolute relative to its parent container and cannot be dragged or resized outside the container boundaries.
            </Text>
          </Stack>
        </Window>
      </Stack>
    </>
  );
}

Custom Position

You can set the initial position of the window using the defaultPosition prop. This is useful when you want the window to appear at a specific location on the screen.

Custom Position

This window starts at x:200, y:150

import { Window } from '@gfazioli/mantine-window';
import { Box, Title } from '@mantine/core';

function Demo() {
  return (
    <Box pos="relative" style={{ width: '100%', height: 500 }}>
      <Window
        title="Custom Position"
        opened
        defaultPosition={{ x: 200, y: 150 }}
        defaultSize={{ width: 400, height: 300 }}
        persistState={false}
        withinPortal={false}
      >
        <Title order={4}>This window starts at x:200, y:150</Title>
      </Window>
    </Box>
  );
}

Custom Size

Set custom initial dimensions for the window using the defaultSize prop. This allows you to control the width and height of the window when it first appears.

Custom Size

This window is 500x400 pixels

import { Window } from '@gfazioli/mantine-window';
import { Box, Title } from '@mantine/core';

function Demo() {
  return (
    <Box pos="relative" style={{ width: '100%', height: 500 }}>
      <Window
        title="Custom Size"
        opened
        defaultPosition={{ x: 50, y: 50 }}
        defaultSize={{ width: 500, height: 400 }}
        persistState={false}
        withinPortal={false}
      >
        <Title order={4}>This window is 500x400 pixels</Title>
      </Window>
    </Box>
  );
}

Viewport-based Position

You can use viewport units (vw for horizontal, vh for vertical) to set responsive positions that adapt to the browser window size. Viewport units are always calculated relative to the browser viewport, regardless of the withinPortal setting.

Note:

When using viewport units (vw/vh), use withinPortal={true} (default) for consistent behavior.

import { Window } from '@gfazioli/mantine-window';
import { Button, Stack, Text, Title } from '@mantine/core';
import { useDisclosure } from '@mantine/hooks';

function Demo() {
  const [opened, { open, close }] = useDisclosure(false);

  return (
    <>
      <Button onClick={open}>Open window</Button>
      <Window
        title="Viewport Position"
        opened={opened}
        onClose={close}
        defaultPosition={{ x: '10vw', y: '15vh' }}
        defaultSize={{ width: 400, height: 300 }}
        persistState={false}
      >
        <Title order={4}>Position with Viewport Units</Title>
        <Stack gap="sm">
          <Text>
            <strong>Position:</strong> x: 10vw, y: 15vh
          </Text>
          <Text>
            <strong>Reference:</strong> Viewport (browser window)
          </Text>
          <Text size="sm" c="dimmed">
            Viewport units (vw/vh) are always relative to the browser viewport. The position adapts
            proportionally when resizing the browser window.
          </Text>
        </Stack>
      </Window>
    </>
  );
}

Percentage Position

Percentage units calculate position relative to the reference container. With withinPortal={false}, percentages are relative to the parent container. The parent container must have position: relative style.

Percentage Position

Position with Percentage

Position: x: 10%, y: 15%

Reference: Parent container (dashed border)

With withinPortal={false}, percentage units are calculated relative to the parent container, not the viewport. The parent must have position: relative.

import { Window } from '@gfazioli/mantine-window';
import { Box, Stack, Text, Title } from '@mantine/core';

function Demo() {
  return (
    <Box pos="relative" style={{ width: '100%', height: 500, border: '2px dashed gray' }}>
      <Window
        title="Percentage Position"
        opened
        defaultPosition={{ x: '10%', y: '15%' }}
        defaultSize={{ width: 400, height: 250 }}
        persistState={false}
        withinPortal={false}
      >
        <Title order={4}>Position with Percentage</Title>
        <Stack gap="sm">
          <Text>
            <strong>Position:</strong> x: 10%, y: 15%
          </Text>
          <Text>
            <strong>Reference:</strong> Parent container (dashed border)
          </Text>
          <Text size="sm" c="dimmed">
            With <code>withinPortal=false</code>, percentage units are calculated relative to the
            parent container, not the viewport. The parent must have <code>position: relative</code>.
          </Text>
        </Stack>
      </Window>
    </Box>
  );
}

Viewport-based Size

Use viewport units (vw for width, vh for height) to create responsive window sizes that scale proportionally with the viewport. Viewport units are always calculated relative to the browser viewport.

Note:

When using viewport units (vw/vh), use withinPortal={true} (default) for consistent behavior.

import { Window } from '@gfazioli/mantine-window';
import { Button, Stack, Text, Title } from '@mantine/core';
import { useDisclosure } from '@mantine/hooks';

function Demo() {
  const [opened, { open, close }] = useDisclosure(false);

  return (
    <>
      <Button onClick={open}>Open window</Button>
      <Window
        title="Viewport Size"
        opened={opened}
        onClose={close}
        defaultPosition={{ x: '5vw', y: '10vh' }}
        defaultSize={{ width: '40vw', height: '50vh' }}
        persistState={false}
      >
        <Title order={4}>Size with Viewport Units</Title>
        <Stack gap="sm">
          <Text>
            <strong>Size:</strong> width: 40vw, height: 50vh
          </Text>
          <Text>
            <strong>Position:</strong> x: 5vw, y: 10vh
          </Text>
          <Text>
            The window size and position scale proportionally with the viewport. Try resizing your
            browser window to see the window adapt!
          </Text>
        </Stack>
      </Window>
    </>
  );
}

Percentage Size

Percentage units for size work differently based on the withinPortal setting. With withinPortal={false}, percentages are calculated relative to the parent container dimensions.

Percentage Size

Size with Percentage

Size: width: 60%, height: 50%

Reference: Parent container (dashed border)

The window size is 60% of container width and 50% of container height. Percentage units with withinPortal={false} are relative to the parent container.

import { Window } from '@gfazioli/mantine-window';
import { Box, Stack, Text, Title } from '@mantine/core';

function Demo() {
  return (
    <Box pos="relative" style={{ width: '100%', height: 500, border: '2px dashed gray' }}>
      <Window
        title="Percentage Size"
        opened
        defaultPosition={{ x: 50, y: 50 }}
        defaultSize={{ width: '60%', height: '50%' }}
        persistState={false}
        withinPortal={false}
      >
        <Title order={4}>Size with Percentage</Title>
        <Stack gap="sm">
          <Text>
            <strong>Size:</strong> width: 60%, height: 50%
          </Text>
          <Text>
            <strong>Reference:</strong> Parent container (dashed border)
          </Text>
          <Text size="sm" c="dimmed">
            The window size is 60% of container width and 50% of container height. Percentage units
            with <code>withinPortal=false</code> are relative to the parent container.
          </Text>
        </Stack>
      </Window>
    </Box>
  );
}

Responsive Windows

Combine viewport units for both position and size to create fully responsive windows. You can mix different unit types (pixels, viewport units, percentages) to achieve the perfect balance of flexibility and control.

Understanding Unit References:

  • vw/vh (viewport units): Always relative to the viewport (browser window), regardless of withinPortal setting
  • % (percentage):
    • With withinPortal={true} (default): relative to the viewport (browser window)
    • With withinPortal={false}: relative to the parent container
  • px (pixels): Always fixed pixels, regardless of withinPortal setting

Best Practices:

  • Use vw/vh with withinPortal={true} (default) for viewport-relative sizing
  • Use % with withinPortal={false} for container-relative sizing
  • Parent container must have position: relative when using withinPortal={false}
import { Window } from '@gfazioli/mantine-window';
import { Button, Stack, Text, Title } from '@mantine/core';
import { useDisclosure } from '@mantine/hooks';

function Demo() {
  const [opened, { open, close }] = useDisclosure(false);

  return (
    <>
      <Button onClick={open}>Open window</Button>
      <Window
        title="Responsive Window"
        opened={opened}
        onClose={close}
        defaultPosition={{ x: '5vw', y: '10vh' }}
        defaultSize={{ width: '50vw', height: '40vh' }}
        minWidth="300px"
        minHeight="200px"
        maxWidth="90vw"
        maxHeight="80vh"
        persistState={false}
      >
        <Title order={4}>Viewport-based Position & Size</Title>
        <Stack gap="sm">
          <Text>
            <strong>Position:</strong> x: 5vw, y: 10vh
          </Text>
          <Text>
            <strong>Size:</strong> width: 50vw, height: 40vh
          </Text>
          <Text>
            <strong>Min:</strong> 300px x 200px
          </Text>
          <Text>
            <strong>Max:</strong> 90vw x 80vh
          </Text>
          <Text size="sm" c="dimmed">
            This window combines viewport units for position and size with pixel-based minimum
            constraints and viewport-based maximum constraints. All units are relative to the
            viewport.
          </Text>
        </Stack>
      </Window>
    </>
  );
}

Min/Max Size Constraints

Control the minimum and maximum dimensions during resize operations using minWidth, minHeight, maxWidth, and maxHeight props. This prevents the window from becoming too small or too large.

Constrained Resize

Try resizing this window

Min: 300x200px

Max: 600x450px

import { Window } from '@gfazioli/mantine-window';
import { Box, Title } from '@mantine/core';

function Demo() {
  return (
    <Box pos="relative" style={{ width: '100%', height: 500 }}>
      <Window
        title="Constrained Resize"
        opened
        defaultPosition={{ x: 50, y: 50 }}
        defaultSize={{ width: 400, height: 300 }}
        minWidth={300}
        minHeight={200}
        maxWidth={600}
        maxHeight={450}
        persistState={false}
        withinPortal={false}
      >
        <Title order={4}>Try resizing this window</Title>
        <p>Min: 300x200px</p>
        <p>Max: 600x450px</p>
      </Window>
    </Box>
  );
}

Viewport-based Constraints

You can use viewport units (vw for width, vh for height) to create responsive constraints that adapt to the browser window size. This is particularly useful for creating windows that scale proportionally with the viewport.

import { Window } from '@gfazioli/mantine-window';
import { Button, Stack, Title } from '@mantine/core';
import { useDisclosure } from '@mantine/hooks';

function Demo() {
  const [opened, { open, close }] = useDisclosure(false);

  return (
    <>
      <Button onClick={open}>Open window</Button>
      <Window
        title="Viewport-based Constraints"
        opened={opened}
        onClose={close}
        defaultPosition={{ x: 50, y: 50 }}
        defaultSize={{ width: 500, height: 350 }}
        minWidth="30vw"
        minHeight="20vh"
        maxWidth="80vw"
        maxHeight="70vh"
        persistState={false}
      >
        <Title order={4}>Viewport-based Min/Max Size</Title>
        <Stack gap="sm">
          <p>
            <strong>Min:</strong> 30vw x 20vh
          </p>
          <p>
            <strong>Max:</strong> 80vw x 70vh
          </p>
          <p>Try resizing your browser window to see the constraints adapt!</p>
        </Stack>
      </Window>
    </>
  );
}

Mixed Unit Constraints

Mix different unit types for more flexible sizing. You can combine fixed pixel values with viewport units to create hybrid constraints that work best for your use case.

import { Window } from '@gfazioli/mantine-window';
import { Button, Stack, Title } from '@mantine/core';
import { useDisclosure } from '@mantine/hooks';

function Demo() {
  const [opened, { open, close }] = useDisclosure(false);

  return (
    <>
      <Button onClick={open}>Open window</Button>
      <Window
        title="Mixed Unit Constraints"
        opened={opened}
        onClose={close}
        defaultPosition={{ x: 50, y: 50 }}
        defaultSize={{ width: 400, height: 350 }}
        minWidth={300}
        minHeight="15vh"
        maxWidth="60vw"
        maxHeight={600}
        persistState={false}
      >
        <Title order={4}>Mixed Units</Title>
        <Stack gap="sm">
          <p>
            <strong>Min Width:</strong> 300px (fixed)
          </p>
          <p>
            <strong>Min Height:</strong> 15vh (viewport-based)
          </p>
          <p>
            <strong>Max Width:</strong> 60vw (viewport-based)
          </p>
          <p>
            <strong>Max Height:</strong> 600px (fixed)
          </p>
        </Stack>
      </Window>
    </>
  );
}

Drag Boundaries

Restrict the draggable area of the window using the dragBounds prop. This ensures the window stays within specific boundaries and cannot be dragged outside the defined area.

Bounded Dragging

Try dragging this window

It can only move within specific bounds:

X: 50-500, Y: 50-400

import { Window } from '@gfazioli/mantine-window';
import { Box, Title } from '@mantine/core';

function Demo() {
  return (
    <Box pos="relative" style={{ width: '100%', height: 500 }}>
      <Window
        title="Bounded Dragging"
        opened
        defaultPosition={{ x: 100, y: 50 }}
        defaultSize={{ width: 400, height: 300 }}
        dragBounds={{
          minX: 50,
          maxX: 500,
          minY: 50,
          maxY: 400,
        }}
        persistState={false}
        withinPortal={false}
      >
        <Title order={4}>Try dragging this window</Title>
        <p>It can only move within specific bounds:</p>
        <p>X: 50-500, Y: 50-400</p>
      </Window>
    </Box>
  );
}

Viewport-based Drag Boundaries

Use viewport units (vw for width, vh for height) to create responsive drag boundaries that adapt to the browser window size. This is useful for ensuring windows stay within proportional boundaries regardless of screen size.

import { Window } from '@gfazioli/mantine-window';
import { Button, Stack, Title } from '@mantine/core';
import { useDisclosure } from '@mantine/hooks';

function Demo() {
  const [opened, { open, close }] = useDisclosure(false);

  return (
    <>
      <Button onClick={open}>Open window</Button>
      <Window
        title="Viewport-based Drag Bounds"
        opened={opened}
        onClose={close}
        defaultPosition={{ x: '10vw', y: '10vh' }}
        defaultSize={{ width: 400, height: 300 }}
        dragBounds={{
          minX: '10vw',
          maxX: '70vw',
          minY: '10vh',
          maxY: '70vh',
        }}
        persistState={false}
      >
        <Title order={4}>Viewport-based Boundaries</Title>
        <Stack gap="sm">
          <p>
            <strong>X Bounds:</strong> 10vw to 70vw
          </p>
          <p>
            <strong>Y Bounds:</strong> 10vh to 70vh
          </p>
          <p>Try dragging this window - it stays within viewport-based boundaries!</p>
          <p>Resize your browser to see bounds adapt.</p>
        </Stack>
      </Window>
    </>
  );
}

Percentage Drag Boundaries

Define drag boundaries using percentage values relative to the viewport or container size. Percentages provide a flexible way to constrain window movement proportionally.

Percentage Drag Bounds

Percentage Boundaries

X Bounds: 5% to 75%

Y Bounds: 5% to 75%

Boundaries are percentage of viewport/container size.

import { Window } from '@gfazioli/mantine-window';
import { Box, Stack, Title } from '@mantine/core';

function Demo() {
  return (
    <Box pos="relative" style={{ width: '100%', height: 500 }}>
      <Window
        title="Percentage Drag Bounds"
        opened
        defaultPosition={{ x: 50, y: 50 }}
        defaultSize={{ width: 350, height: 280 }}
        dragBounds={{
          minX: '5%',
          maxX: '75%',
          minY: '5%',
          maxY: '75%',
        }}
        persistState={false}
        withinPortal={false}
      >
        <Title order={4}>Percentage Boundaries</Title>
        <Stack gap="sm">
          <p>
            <strong>X Bounds:</strong> 5% to 75%
          </p>
          <p>
            <strong>Y Bounds:</strong> 5% to 75%
          </p>
          <p>Boundaries are percentage of viewport/container size.</p>
        </Stack>
      </Window>
    </Box>
  );
}

Mixed Unit Drag Boundaries

Combine different unit types (pixels, viewport units, percentages) for drag boundaries to create sophisticated constraints that meet your specific requirements.

import { Window } from '@gfazioli/mantine-window';
import { Button, Stack, Title } from '@mantine/core';
import { useDisclosure } from '@mantine/hooks';

function Demo() {
  const [opened, { open, close }] = useDisclosure(false);

  return (
    <>
      <Button onClick={open}>Open window</Button>
      <Window
        title="Mixed Unit Drag Bounds"
        opened={opened}
        onClose={close}
        defaultPosition={{ x: 50, y: '10vh' }}
        defaultSize={{ width: 400, height: 300 }}
        dragBounds={{
          minX: 50,
          maxX: '80vw',
          minY: '10vh',
          maxY: 450,
        }}
        persistState={false}
      >
        <Title order={4}>Mixed Unit Boundaries</Title>
        <Stack gap="sm">
          <p>
            <strong>Min X:</strong> 50px (fixed)
          </p>
          <p>
            <strong>Max X:</strong> 80vw (viewport-based)
          </p>
          <p>
            <strong>Min Y:</strong> 10vh (viewport-based)
          </p>
          <p>
            <strong>Max Y:</strong> 450px (fixed)
          </p>
          <p>Mix different unit types for flexible boundaries!</p>
        </Stack>
      </Window>
    </>
  );
}

Persistence

Enable state persistence to save the window's position and size in localStorage by setting persistState={true} (default is true). When enabled, the window will remember its position and size across page refreshes. Set persistState={false} to disable this behavior and always start at the default position and size.

With Persistence

Position and size are saved in localStorage

Move or resize this window, then refresh the page

The window will remember its last position and size

import { Window } from '@gfazioli/mantine-window';
import { Box, Title } from '@mantine/core';

function Demo() {
  return (
    <Box pos="relative" style={{ width: '100%', height: 500 }}>
      <Window
        title="With Persistence"
        id="persistent-window-demo"
        opened
        defaultPosition={{ x: 50, y: 50 }}
        defaultSize={{ width: 400, height: 300 }}
        persistState
        withinPortal={false}
      >
        <Title order={4}>Position and size are saved in localStorage</Title>
        <p>Move or resize this window, then refresh the page</p>
        <p>The window will remember its last position and size</p>
      </Window>
    </Box>
  );
}

Callbacks

Use onPositionChange and onSizeChange callbacks to respond to window movements and resize operations. These callbacks receive the current position or size as arguments.

With Callbacks

Open console to see callbacks

Move or resize this window to trigger callbacks

import { Window } from '@gfazioli/mantine-window';
import { Box, Title } from '@mantine/core';

function Demo() {
  return (
    <Box pos="relative" style={{ width: '100%', height: 500 }}>
      <Window
        title="With Callbacks"
        opened
        defaultPosition={{ x: 50, y: 50 }}
        defaultSize={{ width: 400, height: 300 }}
        persistState={false}
        withinPortal={false}
        onPositionChange={(pos) => {
          // eslint-disable-next-line no-console
          console.log('Position changed:', pos);
        }}
        onSizeChange={(size) => {
          // eslint-disable-next-line no-console
          console.log('Size changed:', size);
        }}
      >
        <Title order={4}>Open console to see callbacks</Title>
        <p>Move or resize this window to trigger callbacks</p>
      </Window>
    </Box>
  );
}

Centered Window

Create a centered, fixed window that cannot be moved or resized by combining defaultPosition, defaultSize, resizable="none", and draggable="none" props.

Centered Window

Centered, fixed window

This window is centered and cannot be moved or resized

import { Window } from '@gfazioli/mantine-window';
import { Box, Title } from '@mantine/core';

function Demo() {
  return (
    <Box pos="relative" style={{ width: '100%', height: 500 }}>
      <Window
        title="Centered Window"
        opened
        defaultPosition={{ x: 190, y: 100 }}
        defaultSize={{ width: 420, height: 300 }}
        resizable="none"
        draggable="none"
        persistState={false}
        withinPortal={false}
      >
        <Title order={4}>Centered, fixed window</Title>
        <p>This window is centered and cannot be moved or resized</p>
      </Window>
    </Box>
  );
}

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

First Window

Click on a window to bring it to front

Window 2

Second Window

Click on a window to bring it to front

import { Window } from '@gfazioli/mantine-window';
import { Box, Title } from '@mantine/core';

function Demo() {
  return (
    <Box pos="relative" style={{ width: '100%', height: 500 }}>
      <Window
        title="Window 1"
        id="demo-window-1"
        opened
        defaultPosition={{ x: 20, y: 20 }}
        defaultSize={{ width: 300, height: 250 }}
        persistState={false}
        withinPortal={false}
      >
        <Title order={4}>First Window</Title>
        <p>Click on a window to bring it to front</p>
      </Window>
      <Window
        title="Window 2"
        id="demo-window-2"
        opened
        defaultPosition={{ x: 180, y: 80 }}
        defaultSize={{ width: 300, height: 250 }}
        persistState={false}
        withinPortal={false}
      >
        <Title order={4}>Second Window</Title>
        <p>Click on a window to bring it to front</p>
      </Window>
    </Box>
  );
}

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

This window cannot be collapsed

Double-clicking the header will not collapse the window

import { Window } from '@gfazioli/mantine-window';
import { Box, Title } from '@mantine/core';

function Demo() {
  return (
    <Box pos="relative" style={{ width: '100%', height: 500 }}>
      <Window
        title="No Collapsable"
        opened
        defaultPosition={{ x: 50, y: 50 }}
        defaultSize={{ width: 400, height: 300 }}
        collapsable={false}
        persistState={false}
        withinPortal={false}
      >
        <Title order={4}>This window cannot be collapsed</Title>
        <p>Double-clicking the header will not collapse the window</p>
      </Window>
    </Box>
  );
}

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.

You can customize the appearance of the Window component using the Styles API. This allows you to modify styles for various parts of the component, such as keys, values, brackets, and more.

Styles API

This is a window with data

Component Styles API

Hover over selectors to highlight corresponding elements

/*
 * Hover over selectors to apply outline styles
 *
 */