{"slug":"floating-panel","title":"Floating Panel","description":"A floating window that contains one or more panes.","contentType":"component","framework":"svelte","content":"A floating panel is a detachable window that floats above the main interface,\ntypically used for displaying and editing properties. The panel can be dragged,\nresized, and positioned anywhere on the screen for optimal workflow.\n\n> Think of the panel that pops up in Figma when you click `variables` or try to\n> set a color.\n\n## Resources\n\n\n[Latest version: v]()\n[Logic Visualizer](https://zag-visualizer.vercel.app/floating-panel)\n[Source Code](https://github.com/chakra-ui/zag/tree/main/packages/machines/floating-panel)\n\n\n\n**Features**\n\n- Allows interaction with the main content\n- Supports dragging and resizing\n- Supports minimizing and maximizing the panel\n- Controlled and uncontrolled size and position\n- Supports snapping to a grid\n- Supports locking the aspect ratio\n- Supports closing on escape key\n- Supports persisting size and position when closed\n\n## Installation\n\nInstall the floating panel package:\n\n```bash\nnpm install @zag-js/floating-panel @zag-js/svelte\n# or\nyarn add @zag-js/floating-panel @zag-js/svelte\n```\n\n## Anatomy\n\nCheck the floating panel anatomy and part names.\n\n> Each part includes a `data-part` attribute to help identify them in the DOM.\n\n\n\n## Usage\n\nImport the floating panel package:\n\n```tsx\nimport * as floatingPanel from \"@zag-js/floating-panel\"\n```\n\nThese are the key exports:\n\n- `machine` - State machine logic.\n- `connect` - Maps behavior to JSX props and event handlers.\n\nThen use the framework integration helpers:\n\n```svelte\n<script lang=\"ts\">\n  import * as floatingPanel from \"@zag-js/floating-panel\"\n  import { normalizeProps, useMachine } from \"@zag-js/svelte\"\n  import { ArrowDownLeft, Maximize2, Minus, XIcon } from \"lucide-svelte\"\n\n  const id = $props.id()\n  const service = useMachine(floatingPanel.machine, { id })\n\n  const api = $derived(floatingPanel.connect(service, normalizeProps))\n</script>\n\n<button {...api.getTriggerProps()}>Toggle Panel</button>\n\n<div {...api.getPositionerProps()}>\n  <div {...api.getContentProps()}>\n    <div {...api.getDragTriggerProps()}>\n      <div {...api.getHeaderProps()}>\n        <p {...api.getTitleProps()}>Floating Panel</p>\n        <div {...api.getControlProps()}>\n          <button {...api.getStageTriggerProps({ stage: \"minimized\" })}>\n            <Minus />\n          </button>\n          <button {...api.getStageTriggerProps({ stage: \"maximized\" })}>\n            <Maximize2 />\n          </button>\n          <button {...api.getStageTriggerProps({ stage: \"default\" })}>\n            <ArrowDownLeft />\n          </button>\n          <button {...api.getCloseTriggerProps()}>\n            <XIcon />\n          </button>\n        </div>\n      </div>\n    </div>\n    <div {...api.getBodyProps()}>\n      <p>Some content</p>\n    </div>\n\n    <div {...api.getResizeTriggerProps({ axis: \"n\" })}></div>\n    <div {...api.getResizeTriggerProps({ axis: \"e\" })}></div>\n    <div {...api.getResizeTriggerProps({ axis: \"w\" })}></div>\n    <div {...api.getResizeTriggerProps({ axis: \"s\" })}></div>\n    <div {...api.getResizeTriggerProps({ axis: \"ne\" })}></div>\n    <div {...api.getResizeTriggerProps({ axis: \"se\" })}></div>\n    <div {...api.getResizeTriggerProps({ axis: \"sw\" })}></div>\n    <div {...api.getResizeTriggerProps({ axis: \"nw\" })}></div>\n  </div>\n</div>\n```\n\n### Resizing\n\n#### Setting the initial size\n\nTo set the initial size of the floating panel, you can pass the `defaultSize`\nprop to the machine.\n\n```tsx {2}\nconst service = useMachine(floatingPanel.machine, {\n  defaultSize: { width: 300, height: 300 },\n})\n```\n\n#### Controlling the size\n\nUse `size` and `onSizeChange` to control size externally.\n\n```tsx {2}\nconst service = useMachine(floatingPanel.machine, {\n  size: { width: 300, height: 300 },\n  onSizeChange(details) {\n    // details => { size: { width: number, height: number } }\n    console.log(\"floating panel is:\", details.size.width, details.size.height)\n  },\n})\n```\n\n#### Disable resizing\n\nBy default, the panel can be resized by dragging its edges (resize handles). To\ndisable this behavior, set the `resizable` prop to `false`.\n\n```tsx {2}\nconst service = useMachine(floatingPanel.machine, {\n  resizable: false,\n})\n```\n\n#### Setting size constraints\n\nYou can also control the minimum allowed dimensions of the panel by using the\n`minSize` and `maxSize` props.\n\n```tsx {2,3}\nconst service = useMachine(floatingPanel.machine, {\n  minSize: { width: 100, height: 100 },\n  maxSize: { width: 500, height: 500 },\n})\n```\n\n### Aspect ratio\n\nTo lock the aspect ratio of the floating panel, set the `lockAspectRatio` prop.\nThis will ensure the panel maintains a consistent aspect ratio while being\nresized.\n\n```tsx {2}\nconst service = useMachine(floatingPanel.machine, {\n  lockAspectRatio: true,\n})\n```\n\n### Positioning\n\n#### Setting the initial position\n\nTo specify the initial position of the floating panel, use the `defaultPosition`\nprop. If `defaultPosition` is not provided, the floating panel will be initially\npositioned at the center of the viewport.\n\n```tsx {2}\nconst service = useMachine(floatingPanel.machine, {\n  defaultPosition: { x: 500, y: 200 },\n})\n```\n\n#### Anchor position\n\nAn alternative to setting the initial position is to provide a function that\nreturns the anchor position. This function is called when the panel is opened\nand receives the `triggerRect` and `boundaryRect`.\n\n```tsx {2}\nconst service = useMachine(floatingPanel.machine, {\n  getAnchorPosition({ triggerRect, boundaryRect }) {\n    return {\n      x: boundaryRect.x + (boundaryRect.width - triggerRect.width) / 2,\n      y: boundaryRect.y + (boundaryRect.height - triggerRect.height) / 2,\n    }\n  },\n})\n```\n\n#### Controlling the position\n\nTo control the position of the floating panel programmatically, you can pass the\n`position` and `onPositionChange` prop to the machine.\n\n```tsx {2}\nconst service = useMachine(floatingPanel.machine, {\n  position: { x: 500, y: 200 },\n  onPositionChange(details) {\n    // details => { position: { x: number, y: number } }\n    console.log(\"floating panel is:\", details.position.x, details.position.y)\n  },\n})\n```\n\n#### Disable dragging\n\nThe floating panel enables you to set its position and move it by dragging. To\ndisable this behavior, set the `draggable` prop to `false`.\n\n```tsx\nconst service = useMachine(floatingPanel.machine, {\n  draggable: false,\n})\n```\n\n#### Controlling positioning strategy\n\nUse `strategy` to control positioning behavior (`fixed` or `absolute`).\n\n```tsx\nconst service = useMachine(floatingPanel.machine, {\n  strategy: \"absolute\",\n})\n```\n\n#### Snapping to a grid\n\nUse `gridSize` to snap drag and resize interactions to a grid.\n\n```tsx\nconst service = useMachine(floatingPanel.machine, {\n  gridSize: 8,\n})\n```\n\n#### Allowing overflow outside boundaries\n\nSet `allowOverflow` to `true` to let the panel move outside boundary limits.\n\n```tsx\nconst service = useMachine(floatingPanel.machine, {\n  allowOverflow: true,\n})\n```\n\n### Events\n\nThe floating panel generates a variety of events that you can handle.\n\n#### Open State\n\nWhen the floating panel is `opened` or `closed`, the `onOpenChange` callback is\ninvoked.\n\n```tsx {2-5}\nconst service = useMachine(floatingPanel.machine, {\n  onOpenChange(details) {\n    // details => { open: boolean }\n    console.log(\"floating panel is:\", details.open ? \"opened\" : \"closed\")\n  },\n})\n```\n\n#### Position Change\n\nWhen the position of the floating panel changes, these callbacks are invoked:\n\n- `onPositionChange` — When the position of the floating panel changes.\n- `onPositionChangeEnd` — When the position of the floating panel changes ends.\n\n```tsx {2-5}\nconst service = useMachine(floatingPanel.machine, {\n  onPositionChange(details) {\n    // details => { position: { x: number, y: number } }\n    console.log(\"floating panel is:\", details.position.x, details.position.y)\n  },\n  onPositionChangeEnd(details) {\n    // details => { position: { x: number, y: number } }\n    console.log(\"floating panel is:\", details.position.x, details.position.y)\n  },\n})\n```\n\n#### Resize\n\nWhen the size of the floating panel changes, these callbacks are invoked:\n\n- `onSizeChange` — When the panel size changes.\n- `onSizeChangeEnd` — When panel resizing ends.\n\n```tsx {2-5}\nconst service = useMachine(floatingPanel.machine, {\n  onSizeChange(details) {\n    // details => { size: { width: number, height: number } }\n    console.log(\"floating panel is:\", details.size.width, details.size.height)\n  },\n  onSizeChangeEnd(details) {\n    // details => { size: { width: number, height: number } }\n    console.log(\"floating panel is:\", details.size.width, details.size.height)\n  },\n})\n```\n\n#### Minimizing and Maximizing\n\nThe floating panel can be minimized, default, and maximized by clicking the\nrespective buttons in the header. We refer to this as the panel's `stage`.\n\n- When the panel is minimized, the body is hidden and the panel is resized to a\n  minimum size.\n\n- When the panel is maximized, the panel scales to the match the size of the\n  defined boundary rect (via `getBoundaryEl` prop).\n\n- When the panel is restored, the panel is resized back to the previously known\n  size.\n\nWhen the stage changes, the `onStageChange` callback is invoked.\n\n```tsx {2-5}\nconst service = useMachine(floatingPanel.machine, {\n  onStageChange(details) {\n    // details => { stage: \"minimized\" | \"maximized\" | \"default\" }\n    console.log(\"floating panel is:\", details.stage)\n  },\n})\n```\n\n### Persisting size and position when closed\n\nSet `persistRect` to `true` to preserve size and position across close and\nreopen.\n\n```tsx\nconst service = useMachine(floatingPanel.machine, {\n  persistRect: true,\n})\n```\n\n### Customizing accessibility labels\n\nUse `translations` to customize stage control labels.\n\n```tsx\nconst service = useMachine(floatingPanel.machine, {\n  translations: {\n    minimize: \"Minimize panel\",\n    maximize: \"Maximize panel\",\n    restore: \"Restore panel\",\n  },\n})\n```\n\n## Styling guide\n\nThe floating panel component uses data attributes to style its various parts.\nEach part has a `data-scope=\"floating-panel\"` and `data-part` attribute that you\ncan use to target specific elements.\n\n```css\n[data-scope=\"floating-panel\"][data-part=\"content\"] {\n  /* Add styles for the main panel container */\n}\n\n[data-scope=\"floating-panel\"][data-part=\"body\"] {\n  /* Add styles for the panel's content area */\n}\n\n[data-scope=\"floating-panel\"][data-part=\"header\"] {\n  /* Add styles for the panel's header */\n}\n\n[data-scope=\"floating-panel\"][data-part=\"stage-trigger\"] {\n  /* Add styles for state buttons in the header */\n}\n\n[data-scope=\"floating-panel\"][data-part=\"resize-trigger\"] {\n  /* Add styles for resize handles */\n}\n\n/* North and south resize handles */\n[data-scope=\"floating-panel\"][data-part=\"resize-trigger\"][data-axis=\"n\"],\n[data-scope=\"floating-panel\"][data-part=\"resize-trigger\"][data-axis=\"s\"] {\n  /* Add styles for north and south resize handles */\n}\n\n/* East and west resize handles */\n[data-scope=\"floating-panel\"][data-part=\"resize-trigger\"][data-axis=\"e\"],\n[data-scope=\"floating-panel\"][data-part=\"resize-trigger\"][data-axis=\"w\"] {\n  /* Add styles for east and west resize handles */\n}\n\n/* Corner resize handles */\n[data-scope=\"floating-panel\"][data-part=\"resize-trigger\"][data-axis=\"ne\"],\n[data-scope=\"floating-panel\"][data-part=\"resize-trigger\"][data-axis=\"nw\"],\n[data-scope=\"floating-panel\"][data-part=\"resize-trigger\"][data-axis=\"se\"],\n[data-scope=\"floating-panel\"][data-part=\"resize-trigger\"][data-axis=\"sw\"] {\n  /* Add styles for corner resize handles */\n}\n```\n\n### Dragging\n\nWhen dragging the panel, the `[data-dragging]` attribute is applied to the\npanel.\n\n```css\n[data-scope=\"floating-panel\"][data-part=\"content\"][data-dragging] {\n  /* Add styles for dragging state */\n}\n```\n\n### Stacking\n\nThe floating panel has several states that can be targeted using data\nattributes:\n\n```css\n/* When the panel is the topmost element */\n[data-scope=\"floating-panel\"][data-part=\"content\"][data-topmost] {\n  /* Add styles for topmost state */\n}\n\n/* When the panel is behind another panel */\n[data-scope=\"floating-panel\"][data-part=\"content\"][data-behind] {\n  /* Add styles for behind state */\n}\n```\n\n## Methods and Properties\n\n### Machine Context\n\nThe floating panel machine exposes the following context properties:\n\n**`ids`**\nType: `Partial<{ trigger: string; positioner: string; content: string; title: string; header: string; }>`\nDescription: The ids of the elements in the floating panel. Useful for composition.\n\n**`translations`**\nType: `IntlTranslations`\nDescription: The translations for the floating panel.\n\n**`strategy`**\nType: `\"absolute\" | \"fixed\"`\nDescription: The strategy to use for positioning\n\n**`allowOverflow`**\nType: `boolean`\nDescription: Whether the panel should be strictly contained within the boundary when dragging\n\n**`open`**\nType: `boolean`\nDescription: The controlled open state of the panel\n\n**`defaultOpen`**\nType: `boolean`\nDescription: The initial open state of the panel when rendered.\nUse when you don't need to control the open state of the panel.\n\n**`draggable`**\nType: `boolean`\nDescription: Whether the panel is draggable\n\n**`resizable`**\nType: `boolean`\nDescription: Whether the panel is resizable\n\n**`size`**\nType: `Size`\nDescription: The size of the panel\n\n**`defaultSize`**\nType: `Size`\nDescription: The default size of the panel\n\n**`minSize`**\nType: `Size`\nDescription: The minimum size of the panel\n\n**`maxSize`**\nType: `Size`\nDescription: The maximum size of the panel\n\n**`position`**\nType: `Point`\nDescription: The controlled position of the panel\n\n**`defaultPosition`**\nType: `Point`\nDescription: The initial position of the panel when rendered.\nUse when you don't need to control the position of the panel.\n\n**`getAnchorPosition`**\nType: `(details: AnchorPositionDetails) => Point`\nDescription: Function that returns the initial position of the panel when it is opened.\nIf provided, will be used instead of the default position.\n\n**`lockAspectRatio`**\nType: `boolean`\nDescription: Whether the panel is locked to its aspect ratio\n\n**`closeOnEscape`**\nType: `boolean`\nDescription: Whether the panel should close when the escape key is pressed\n\n**`getBoundaryEl`**\nType: `() => HTMLElement`\nDescription: The boundary of the panel. Useful for recalculating the boundary rect when\nthe it is resized.\n\n**`disabled`**\nType: `boolean`\nDescription: Whether the panel is disabled\n\n**`onPositionChange`**\nType: `(details: PositionChangeDetails) => void`\nDescription: Function called when the position of the panel changes via dragging\n\n**`onPositionChangeEnd`**\nType: `(details: PositionChangeDetails) => void`\nDescription: Function called when the position of the panel changes via dragging ends\n\n**`onOpenChange`**\nType: `(details: OpenChangeDetails) => void`\nDescription: Function called when the panel is opened or closed\n\n**`onSizeChange`**\nType: `(details: SizeChangeDetails) => void`\nDescription: Function called when the size of the panel changes via resizing\n\n**`onSizeChangeEnd`**\nType: `(details: SizeChangeDetails) => void`\nDescription: Function called when the size of the panel changes via resizing ends\n\n**`persistRect`**\nType: `boolean`\nDescription: Whether the panel size and position should be preserved when it is closed\n\n**`gridSize`**\nType: `number`\nDescription: The snap grid for the panel\n\n**`onStageChange`**\nType: `(details: StageChangeDetails) => void`\nDescription: Function called when the stage of the panel changes\n\n**`dir`**\nType: `\"ltr\" | \"rtl\"`\nDescription: The document's text/writing direction.\n\n**`id`**\nType: `string`\nDescription: The unique identifier of the machine.\n\n**`getRootNode`**\nType: `() => Node | ShadowRoot | Document`\nDescription: A root node to correctly resolve document in custom environments. E.x.: Iframes, Electron.\n\n### Machine API\n\nThe floating panel `api` exposes the following methods:\n\n**`open`**\nType: `boolean`\nDescription: Whether the panel is open\n\n**`setOpen`**\nType: `(open: boolean) => void`\nDescription: Function to open or close the panel\n\n**`dragging`**\nType: `boolean`\nDescription: Whether the panel is being dragged\n\n**`resizing`**\nType: `boolean`\nDescription: Whether the panel is being resized\n\n**`position`**\nType: `Point`\nDescription: The position of the panel\n\n**`setPosition`**\nType: `(position: Point) => void`\nDescription: Function to set the position of the panel\n\n**`size`**\nType: `Size`\nDescription: The size of the panel\n\n**`setSize`**\nType: `(size: Size) => void`\nDescription: Function to set the size of the panel\n\n**`minimize`**\nType: `VoidFunction`\nDescription: Function to minimize the panel\n\n**`maximize`**\nType: `VoidFunction`\nDescription: Function to maximize the panel\n\n**`restore`**\nType: `VoidFunction`\nDescription: Function to restore the panel before it was minimized or maximized\n\n**`resizable`**\nType: `boolean`\nDescription: Whether the panel is resizable\n\n**`draggable`**\nType: `boolean`\nDescription: Whether the panel is draggable\n\n### Data Attributes\n\n**`Trigger`**\n\n**`data-scope`**: floating-panel\n**`data-part`**: trigger\n**`data-state`**: \"open\" | \"closed\"\n**`data-dragging`**: Present when in the dragging state\n\n**`Content`**\n\n**`data-scope`**: floating-panel\n**`data-part`**: content\n**`data-state`**: \"open\" | \"closed\"\n**`data-dragging`**: Present when in the dragging state\n**`data-topmost`**: Present when topmost\n**`data-behind`**: Present when not topmost\n**`data-minimized`**: Present when minimized\n**`data-maximized`**: Present when maximized\n**`data-staged`**: Present when not in default stage\n\n**`StageTrigger`**\n\n**`data-scope`**: floating-panel\n**`data-part`**: stage-trigger\n**`data-stage`**: The stage of the stagetrigger\n\n**`ResizeTrigger`**\n\n**`data-scope`**: floating-panel\n**`data-part`**: resize-trigger\n**`data-disabled`**: Present when disabled\n**`data-axis`**: The axis to resize\n\n**`DragTrigger`**\n\n**`data-scope`**: floating-panel\n**`data-part`**: drag-trigger\n**`data-disabled`**: Present when disabled\n\n**`Control`**\n\n**`data-scope`**: floating-panel\n**`data-part`**: control\n**`data-disabled`**: Present when disabled\n**`data-stage`**: The stage of the control\n**`data-minimized`**: Present when minimized\n**`data-maximized`**: Present when maximized\n**`data-staged`**: Present when not in default stage\n\n**`Header`**\n\n**`data-scope`**: floating-panel\n**`data-part`**: header\n**`data-dragging`**: Present when in the dragging state\n**`data-topmost`**: Present when topmost\n**`data-behind`**: Present when not topmost\n**`data-minimized`**: Present when minimized\n**`data-maximized`**: Present when maximized\n**`data-staged`**: Present when not in default stage\n\n**`Body`**\n\n**`data-scope`**: floating-panel\n**`data-part`**: body\n**`data-dragging`**: Present when in the dragging state\n**`data-minimized`**: Present when minimized\n**`data-maximized`**: Present when maximized\n**`data-staged`**: Present when not in default stage\n\n### CSS Variables\n\n<CssVarTable name=\"floating-panel\" />","editUrl":"https://github.com/chakra-ui/zag/edit/main/website/data/components/floating-panel.mdx"}