Floating Panel
Used to display content in a non-modal floating window.
Anatomy
To set up the editable correctly, you'll need to understand its anatomy and how we name its parts.
Each part includes a
data-partattribute to help identify them in the DOM.
Examples
Learn how to use the FloatingPanel component in your project. Let's take a look at the most basic example:
import { FloatingPanel } from '@ark-ui/react/floating-panel'
import { Portal } from '@ark-ui/react/portal'
import { ArrowDownLeft, Maximize2, Minus, XIcon } from 'lucide-react'
export const Basic = () => (
<FloatingPanel.Root>
<FloatingPanel.Trigger>Toggle Panel</FloatingPanel.Trigger>
<Portal>
<FloatingPanel.Positioner>
<FloatingPanel.Content>
<FloatingPanel.DragTrigger>
<FloatingPanel.Header>
<FloatingPanel.Title>Floating Panel</FloatingPanel.Title>
<FloatingPanel.Control>
<FloatingPanel.StageTrigger stage="minimized">
<Minus />
</FloatingPanel.StageTrigger>
<FloatingPanel.StageTrigger stage="maximized">
<Maximize2 />
</FloatingPanel.StageTrigger>
<FloatingPanel.StageTrigger stage="default">
<ArrowDownLeft />
</FloatingPanel.StageTrigger>
<FloatingPanel.CloseTrigger>
<XIcon />
</FloatingPanel.CloseTrigger>
</FloatingPanel.Control>
</FloatingPanel.Header>
</FloatingPanel.DragTrigger>
<FloatingPanel.Body>
<p>Some content</p>
</FloatingPanel.Body>
<FloatingPanel.ResizeTrigger axis="n" />
<FloatingPanel.ResizeTrigger axis="e" />
<FloatingPanel.ResizeTrigger axis="w" />
<FloatingPanel.ResizeTrigger axis="s" />
<FloatingPanel.ResizeTrigger axis="ne" />
<FloatingPanel.ResizeTrigger axis="se" />
<FloatingPanel.ResizeTrigger axis="sw" />
<FloatingPanel.ResizeTrigger axis="nw" />
</FloatingPanel.Content>
</FloatingPanel.Positioner>
</Portal>
</FloatingPanel.Root>
)
import { FloatingPanel } from '@ark-ui/solid/floating-panel'
import { ArrowDownLeft, Maximize2, Minus, XIcon } from 'lucide-solid'
import { Portal } from 'solid-js/web'
export const Basic = () => (
<FloatingPanel.Root>
<FloatingPanel.Trigger>Toggle Panel</FloatingPanel.Trigger>
<Portal>
<FloatingPanel.Positioner>
<FloatingPanel.Content>
<FloatingPanel.DragTrigger>
<FloatingPanel.Header>
<FloatingPanel.Title>Floating Panel</FloatingPanel.Title>
<FloatingPanel.Control>
<FloatingPanel.StageTrigger stage="minimized">
<Minus />
</FloatingPanel.StageTrigger>
<FloatingPanel.StageTrigger stage="maximized">
<Maximize2 />
</FloatingPanel.StageTrigger>
<FloatingPanel.StageTrigger stage="default">
<ArrowDownLeft />
</FloatingPanel.StageTrigger>
<FloatingPanel.CloseTrigger>
<XIcon />
</FloatingPanel.CloseTrigger>
</FloatingPanel.Control>
</FloatingPanel.Header>
</FloatingPanel.DragTrigger>
<FloatingPanel.Body>
<p>Some content</p>
</FloatingPanel.Body>
<FloatingPanel.ResizeTrigger axis="n" />
<FloatingPanel.ResizeTrigger axis="e" />
<FloatingPanel.ResizeTrigger axis="w" />
<FloatingPanel.ResizeTrigger axis="s" />
<FloatingPanel.ResizeTrigger axis="ne" />
<FloatingPanel.ResizeTrigger axis="se" />
<FloatingPanel.ResizeTrigger axis="sw" />
<FloatingPanel.ResizeTrigger axis="nw" />
</FloatingPanel.Content>
</FloatingPanel.Positioner>
</Portal>
</FloatingPanel.Root>
)
<script setup lang="ts">
import { FloatingPanel } from '@ark-ui/vue/floating-panel'
import { ArrowDownLeft, Maximize2, Minus, XIcon } from 'lucide-vue-next'
</script>
<template>
<FloatingPanel.Root>
<FloatingPanel.Trigger>Toggle Panel</FloatingPanel.Trigger>
<Teleport to="body">
<FloatingPanel.Positioner>
<FloatingPanel.Content>
<FloatingPanel.DragTrigger>
<FloatingPanel.Header>
<FloatingPanel.Title>Floating Panel</FloatingPanel.Title>
<FloatingPanel.Control>
<FloatingPanel.StageTrigger stage="minimized">
<Minus />
</FloatingPanel.StageTrigger>
<FloatingPanel.StageTrigger stage="maximized">
<Maximize2 />
</FloatingPanel.StageTrigger>
<FloatingPanel.StageTrigger stage="default">
<ArrowDownLeft />
</FloatingPanel.StageTrigger>
<FloatingPanel.CloseTrigger>
<XIcon />
</FloatingPanel.CloseTrigger>
</FloatingPanel.Control>
</FloatingPanel.Header>
</FloatingPanel.DragTrigger>
<FloatingPanel.Body>
<p>Some content</p>
</FloatingPanel.Body>
<FloatingPanel.ResizeTrigger axis="n" />
<FloatingPanel.ResizeTrigger axis="e" />
<FloatingPanel.ResizeTrigger axis="w" />
<FloatingPanel.ResizeTrigger axis="s" />
<FloatingPanel.ResizeTrigger axis="ne" />
<FloatingPanel.ResizeTrigger axis="se" />
<FloatingPanel.ResizeTrigger axis="sw" />
<FloatingPanel.ResizeTrigger axis="nw" />
</FloatingPanel.Content>
</FloatingPanel.Positioner>
</Teleport>
</FloatingPanel.Root>
</template>
Controlling the size
To control the size of the floating panel programmatically, you can pass the size onResize prop to the machine.
import { FloatingPanel } from '@ark-ui/react/floating-panel'
import { Portal } from '@ark-ui/react/portal'
import { ArrowDownLeft, Maximize2, Minus, XIcon } from 'lucide-react'
import { useState } from 'react'
export const ControlledSize = () => {
const [size, setSize] = useState({ width: 400, height: 300 })
return (
<FloatingPanel.Root size={size} onSizeChange={(e) => setSize(e.size)}>
<FloatingPanel.Trigger>Toggle Panel</FloatingPanel.Trigger>
<Portal>
<FloatingPanel.Positioner>
<FloatingPanel.Content>
<FloatingPanel.DragTrigger>
<FloatingPanel.Header>
<FloatingPanel.Title>Floating Panel</FloatingPanel.Title>
<FloatingPanel.Control>
<FloatingPanel.StageTrigger stage="minimized">
<Minus />
</FloatingPanel.StageTrigger>
<FloatingPanel.StageTrigger stage="maximized">
<Maximize2 />
</FloatingPanel.StageTrigger>
<FloatingPanel.StageTrigger stage="default">
<ArrowDownLeft />
</FloatingPanel.StageTrigger>
<FloatingPanel.CloseTrigger>
<XIcon />
</FloatingPanel.CloseTrigger>
</FloatingPanel.Control>
</FloatingPanel.Header>
</FloatingPanel.DragTrigger>
<FloatingPanel.Body>
<p>Some content</p>
</FloatingPanel.Body>
<FloatingPanel.ResizeTrigger axis="n" />
<FloatingPanel.ResizeTrigger axis="e" />
<FloatingPanel.ResizeTrigger axis="w" />
<FloatingPanel.ResizeTrigger axis="s" />
<FloatingPanel.ResizeTrigger axis="ne" />
<FloatingPanel.ResizeTrigger axis="se" />
<FloatingPanel.ResizeTrigger axis="sw" />
<FloatingPanel.ResizeTrigger axis="nw" />
</FloatingPanel.Content>
</FloatingPanel.Positioner>
</Portal>
</FloatingPanel.Root>
)
}
import { FloatingPanel } from '@ark-ui/solid/floating-panel'
import { ArrowDownLeft, Maximize2, Minus, XIcon } from 'lucide-solid'
import { createSignal } from 'solid-js'
import { Portal } from 'solid-js/web'
export const ControlledSize = () => {
const [size, setSize] = createSignal({ width: 400, height: 300 })
return (
<FloatingPanel.Root size={size()} onSizeChange={(e) => setSize(e.size)}>
<FloatingPanel.Trigger>Toggle Panel</FloatingPanel.Trigger>
<Portal>
<FloatingPanel.Positioner>
<FloatingPanel.Content>
<FloatingPanel.DragTrigger>
<FloatingPanel.Header>
<FloatingPanel.Title>Floating Panel</FloatingPanel.Title>
<FloatingPanel.Control>
<FloatingPanel.StageTrigger stage="minimized">
<Minus />
</FloatingPanel.StageTrigger>
<FloatingPanel.StageTrigger stage="maximized">
<Maximize2 />
</FloatingPanel.StageTrigger>
<FloatingPanel.StageTrigger stage="default">
<ArrowDownLeft />
</FloatingPanel.StageTrigger>
<FloatingPanel.CloseTrigger>
<XIcon />
</FloatingPanel.CloseTrigger>
</FloatingPanel.Control>
</FloatingPanel.Header>
</FloatingPanel.DragTrigger>
<FloatingPanel.Body>
<p>Some content</p>
</FloatingPanel.Body>
<FloatingPanel.ResizeTrigger axis="n" />
<FloatingPanel.ResizeTrigger axis="e" />
<FloatingPanel.ResizeTrigger axis="w" />
<FloatingPanel.ResizeTrigger axis="s" />
<FloatingPanel.ResizeTrigger axis="ne" />
<FloatingPanel.ResizeTrigger axis="se" />
<FloatingPanel.ResizeTrigger axis="sw" />
<FloatingPanel.ResizeTrigger axis="nw" />
</FloatingPanel.Content>
</FloatingPanel.Positioner>
</Portal>
</FloatingPanel.Root>
)
}
<script setup lang="ts">
import { FloatingPanel } from '@ark-ui/vue/floating-panel'
import { ArrowDownLeft, Maximize2, Minus, XIcon } from 'lucide-vue-next'
import { ref } from 'vue'
const size = ref({ width: 400, height: 300 })
</script>
<template>
<FloatingPanel.Root v-model:size="size">
<FloatingPanel.Trigger>Toggle Panel</FloatingPanel.Trigger>
<Teleport to="body">
<FloatingPanel.Positioner>
<FloatingPanel.Content>
<FloatingPanel.DragTrigger>
<FloatingPanel.Header>
<FloatingPanel.Title>Floating Panel</FloatingPanel.Title>
<FloatingPanel.Control>
<FloatingPanel.StageTrigger stage="minimized">
<Minus />
</FloatingPanel.StageTrigger>
<FloatingPanel.StageTrigger stage="maximized">
<Maximize2 />
</FloatingPanel.StageTrigger>
<FloatingPanel.StageTrigger stage="default">
<ArrowDownLeft />
</FloatingPanel.StageTrigger>
<FloatingPanel.CloseTrigger>
<XIcon />
</FloatingPanel.CloseTrigger>
</FloatingPanel.Control>
</FloatingPanel.Header>
</FloatingPanel.DragTrigger>
<FloatingPanel.Body>
<p>Some content</p>
</FloatingPanel.Body>
<FloatingPanel.ResizeTrigger axis="n" />
<FloatingPanel.ResizeTrigger axis="e" />
<FloatingPanel.ResizeTrigger axis="w" />
<FloatingPanel.ResizeTrigger axis="s" />
<FloatingPanel.ResizeTrigger axis="ne" />
<FloatingPanel.ResizeTrigger axis="se" />
<FloatingPanel.ResizeTrigger axis="sw" />
<FloatingPanel.ResizeTrigger axis="nw" />
</FloatingPanel.Content>
</FloatingPanel.Positioner>
</Teleport>
</FloatingPanel.Root>
</template>
Controlling the position
To control the position of the floating panel programmatically, you can pass the position and onPositionChange prop
to the machine.
import { FloatingPanel } from '@ark-ui/react/floating-panel'
import { Portal } from '@ark-ui/react/portal'
import { ArrowDownLeft, Maximize2, Minus, XIcon } from 'lucide-react'
import { useState } from 'react'
export const ControlledPosition = () => {
const [position, setPosition] = useState({ x: 200, y: 200 })
return (
<FloatingPanel.Root position={position} onPositionChange={(e) => setPosition(e.position)}>
<FloatingPanel.Trigger>Toggle Panel</FloatingPanel.Trigger>
<Portal>
<FloatingPanel.Positioner>
<FloatingPanel.Content>
<FloatingPanel.DragTrigger>
<FloatingPanel.Header>
<FloatingPanel.Title>Floating Panel</FloatingPanel.Title>
<FloatingPanel.Control>
<FloatingPanel.StageTrigger stage="minimized">
<Minus />
</FloatingPanel.StageTrigger>
<FloatingPanel.StageTrigger stage="maximized">
<Maximize2 />
</FloatingPanel.StageTrigger>
<FloatingPanel.StageTrigger stage="default">
<ArrowDownLeft />
</FloatingPanel.StageTrigger>
<FloatingPanel.CloseTrigger>
<XIcon />
</FloatingPanel.CloseTrigger>
</FloatingPanel.Control>
</FloatingPanel.Header>
</FloatingPanel.DragTrigger>
<FloatingPanel.Body>
<p>Some content</p>
</FloatingPanel.Body>
<FloatingPanel.ResizeTrigger axis="n" />
<FloatingPanel.ResizeTrigger axis="e" />
<FloatingPanel.ResizeTrigger axis="w" />
<FloatingPanel.ResizeTrigger axis="s" />
<FloatingPanel.ResizeTrigger axis="ne" />
<FloatingPanel.ResizeTrigger axis="se" />
<FloatingPanel.ResizeTrigger axis="sw" />
<FloatingPanel.ResizeTrigger axis="nw" />
</FloatingPanel.Content>
</FloatingPanel.Positioner>
</Portal>
</FloatingPanel.Root>
)
}
import { FloatingPanel } from '@ark-ui/solid/floating-panel'
import { ArrowDownLeft, Maximize2, Minus, XIcon } from 'lucide-solid'
import { createSignal } from 'solid-js'
import { Portal } from 'solid-js/web'
export const ControlledPosition = () => {
const [position, setPosition] = createSignal({ x: 200, y: 200 })
return (
<FloatingPanel.Root position={position()} onPositionChange={(e) => setPosition(e.position)}>
<FloatingPanel.Trigger>Toggle Panel</FloatingPanel.Trigger>
<Portal>
<FloatingPanel.Positioner>
<FloatingPanel.Content>
<FloatingPanel.DragTrigger>
<FloatingPanel.Header>
<FloatingPanel.Title>Floating Panel</FloatingPanel.Title>
<FloatingPanel.Control>
<FloatingPanel.StageTrigger stage="minimized">
<Minus />
</FloatingPanel.StageTrigger>
<FloatingPanel.StageTrigger stage="maximized">
<Maximize2 />
</FloatingPanel.StageTrigger>
<FloatingPanel.StageTrigger stage="default">
<ArrowDownLeft />
</FloatingPanel.StageTrigger>
<FloatingPanel.CloseTrigger>
<XIcon />
</FloatingPanel.CloseTrigger>
</FloatingPanel.Control>
</FloatingPanel.Header>
</FloatingPanel.DragTrigger>
<FloatingPanel.Body>
<p>Some content</p>
</FloatingPanel.Body>
<FloatingPanel.ResizeTrigger axis="n" />
<FloatingPanel.ResizeTrigger axis="e" />
<FloatingPanel.ResizeTrigger axis="w" />
<FloatingPanel.ResizeTrigger axis="s" />
<FloatingPanel.ResizeTrigger axis="ne" />
<FloatingPanel.ResizeTrigger axis="se" />
<FloatingPanel.ResizeTrigger axis="sw" />
<FloatingPanel.ResizeTrigger axis="nw" />
</FloatingPanel.Content>
</FloatingPanel.Positioner>
</Portal>
</FloatingPanel.Root>
)
}
<script setup lang="ts">
import { FloatingPanel } from '@ark-ui/vue/floating-panel'
import { ArrowDownLeft, Maximize2, Minus, XIcon } from 'lucide-vue-next'
import { ref } from 'vue'
const position = ref({ x: 200, y: 200 })
</script>
<template>
<FloatingPanel.Root v-model:position="position">
<FloatingPanel.Trigger>Toggle Panel</FloatingPanel.Trigger>
<Teleport to="body">
<FloatingPanel.Positioner>
<FloatingPanel.Content>
<FloatingPanel.DragTrigger>
<FloatingPanel.Header>
<FloatingPanel.Title>Floating Panel</FloatingPanel.Title>
<FloatingPanel.Control>
<FloatingPanel.StageTrigger stage="minimized">
<Minus />
</FloatingPanel.StageTrigger>
<FloatingPanel.StageTrigger stage="maximized">
<Maximize2 />
</FloatingPanel.StageTrigger>
<FloatingPanel.StageTrigger stage="default">
<ArrowDownLeft />
</FloatingPanel.StageTrigger>
<FloatingPanel.CloseTrigger>
<XIcon />
</FloatingPanel.CloseTrigger>
</FloatingPanel.Control>
</FloatingPanel.Header>
</FloatingPanel.DragTrigger>
<FloatingPanel.Body>
<p>Some content</p>
</FloatingPanel.Body>
<FloatingPanel.ResizeTrigger axis="n" />
<FloatingPanel.ResizeTrigger axis="e" />
<FloatingPanel.ResizeTrigger axis="w" />
<FloatingPanel.ResizeTrigger axis="s" />
<FloatingPanel.ResizeTrigger axis="ne" />
<FloatingPanel.ResizeTrigger axis="se" />
<FloatingPanel.ResizeTrigger axis="sw" />
<FloatingPanel.ResizeTrigger axis="nw" />
</FloatingPanel.Content>
</FloatingPanel.Positioner>
</Teleport>
</FloatingPanel.Root>
</template>
Anchor position
Use the getAnchorPosition function to compute the initial position of the floating panel. This function is called when
the panel is opened and receives the triggerRect and boundaryRect.
import { FloatingPanel } from '@ark-ui/react/floating-panel'
import { Portal } from '@ark-ui/react/portal'
import { ArrowDownLeft, Maximize2, Minus, XIcon } from 'lucide-react'
export const AnchorPosition = () => (
<FloatingPanel.Root
getAnchorPosition={({ triggerRect }) => {
if (!triggerRect) return { x: 0, y: 0 }
return {
x: triggerRect.x + triggerRect.width / 2,
y: triggerRect.y + triggerRect.height / 2,
}
}}
>
<FloatingPanel.Trigger>Toggle Panel</FloatingPanel.Trigger>
<Portal>
<FloatingPanel.Positioner>
<FloatingPanel.Content>
<FloatingPanel.DragTrigger>
<FloatingPanel.Header>
<FloatingPanel.Title>Floating Panel</FloatingPanel.Title>
<FloatingPanel.Control>
<FloatingPanel.StageTrigger stage="minimized">
<Minus />
</FloatingPanel.StageTrigger>
<FloatingPanel.StageTrigger stage="maximized">
<Maximize2 />
</FloatingPanel.StageTrigger>
<FloatingPanel.StageTrigger stage="default">
<ArrowDownLeft />
</FloatingPanel.StageTrigger>
<FloatingPanel.CloseTrigger>
<XIcon />
</FloatingPanel.CloseTrigger>
</FloatingPanel.Control>
</FloatingPanel.Header>
</FloatingPanel.DragTrigger>
<FloatingPanel.Body>
<p>Some content</p>
</FloatingPanel.Body>
<FloatingPanel.ResizeTrigger axis="n" />
<FloatingPanel.ResizeTrigger axis="e" />
<FloatingPanel.ResizeTrigger axis="w" />
<FloatingPanel.ResizeTrigger axis="s" />
<FloatingPanel.ResizeTrigger axis="ne" />
<FloatingPanel.ResizeTrigger axis="se" />
<FloatingPanel.ResizeTrigger axis="sw" />
<FloatingPanel.ResizeTrigger axis="nw" />
</FloatingPanel.Content>
</FloatingPanel.Positioner>
</Portal>
</FloatingPanel.Root>
)
import { FloatingPanel } from '@ark-ui/solid/floating-panel'
import { ArrowDownLeft, Maximize2, Minus, XIcon } from 'lucide-solid'
import { Portal } from 'solid-js/web'
export const AnchorPosition = () => (
<FloatingPanel.Root
getAnchorPosition={({ triggerRect }) => {
if (!triggerRect) return { x: 0, y: 0 }
return {
x: triggerRect.x + triggerRect.width / 2,
y: triggerRect.y + triggerRect.height / 2,
}
}}
>
<FloatingPanel.Trigger>Toggle Panel</FloatingPanel.Trigger>
<Portal>
<FloatingPanel.Positioner>
<FloatingPanel.Content>
<FloatingPanel.DragTrigger>
<FloatingPanel.Header>
<FloatingPanel.Title>Floating Panel</FloatingPanel.Title>
<FloatingPanel.Control>
<FloatingPanel.StageTrigger stage="minimized">
<Minus />
</FloatingPanel.StageTrigger>
<FloatingPanel.StageTrigger stage="maximized">
<Maximize2 />
</FloatingPanel.StageTrigger>
<FloatingPanel.StageTrigger stage="default">
<ArrowDownLeft />
</FloatingPanel.StageTrigger>
<FloatingPanel.CloseTrigger>
<XIcon />
</FloatingPanel.CloseTrigger>
</FloatingPanel.Control>
</FloatingPanel.Header>
</FloatingPanel.DragTrigger>
<FloatingPanel.Body>
<p>Some content</p>
</FloatingPanel.Body>
<FloatingPanel.ResizeTrigger axis="n" />
<FloatingPanel.ResizeTrigger axis="e" />
<FloatingPanel.ResizeTrigger axis="w" />
<FloatingPanel.ResizeTrigger axis="s" />
<FloatingPanel.ResizeTrigger axis="ne" />
<FloatingPanel.ResizeTrigger axis="se" />
<FloatingPanel.ResizeTrigger axis="sw" />
<FloatingPanel.ResizeTrigger axis="nw" />
</FloatingPanel.Content>
</FloatingPanel.Positioner>
</Portal>
</FloatingPanel.Root>
)
<script setup lang="ts">
import { FloatingPanel } from '@ark-ui/vue/floating-panel'
import { ArrowDownLeft, Maximize2, Minus, XIcon } from 'lucide-vue-next'
interface Rect {
x: number
y: number
width: number
height: number
}
interface AnchorPositionDetails {
triggerRect: Rect | null
boundaryRect: Rect | null
}
const getAnchorPosition = ({ triggerRect }: AnchorPositionDetails) => {
if (!triggerRect) return { x: 0, y: 0 }
return {
x: triggerRect.x + triggerRect.width / 2,
y: triggerRect.y + triggerRect.height / 2,
}
}
</script>
<template>
<FloatingPanel.Root :get-anchor-position="getAnchorPosition">
<FloatingPanel.Trigger>Toggle Panel</FloatingPanel.Trigger>
<Teleport to="body">
<FloatingPanel.Positioner>
<FloatingPanel.Content>
<FloatingPanel.DragTrigger>
<FloatingPanel.Header>
<FloatingPanel.Title>Floating Panel</FloatingPanel.Title>
<FloatingPanel.Control>
<FloatingPanel.StageTrigger stage="minimized">
<Minus />
</FloatingPanel.StageTrigger>
<FloatingPanel.StageTrigger stage="maximized">
<Maximize2 />
</FloatingPanel.StageTrigger>
<FloatingPanel.StageTrigger stage="default">
<ArrowDownLeft />
</FloatingPanel.StageTrigger>
<FloatingPanel.CloseTrigger>
<XIcon />
</FloatingPanel.CloseTrigger>
</FloatingPanel.Control>
</FloatingPanel.Header>
</FloatingPanel.DragTrigger>
<FloatingPanel.Body>
<p>Some content</p>
</FloatingPanel.Body>
<FloatingPanel.ResizeTrigger axis="n" />
<FloatingPanel.ResizeTrigger axis="e" />
<FloatingPanel.ResizeTrigger axis="w" />
<FloatingPanel.ResizeTrigger axis="s" />
<FloatingPanel.ResizeTrigger axis="ne" />
<FloatingPanel.ResizeTrigger axis="se" />
<FloatingPanel.ResizeTrigger axis="sw" />
<FloatingPanel.ResizeTrigger axis="nw" />
</FloatingPanel.Content>
</FloatingPanel.Positioner>
</Teleport>
</FloatingPanel.Root>
</template>
Controlling the open state
To control the open state of the floating panel programmatically, you can pass the open and onOpenChange prop to the
machine.
import { FloatingPanel } from '@ark-ui/react/floating-panel'
import { Portal } from '@ark-ui/react/portal'
import { ArrowDownLeft, Maximize2, Minus, XIcon } from 'lucide-react'
import { useState } from 'react'
export const ControlledOpen = () => {
const [open, setOpen] = useState(false)
return (
<FloatingPanel.Root open={open} onOpenChange={(e) => setOpen(e.open)}>
<FloatingPanel.Trigger>Toggle Panel</FloatingPanel.Trigger>
<Portal>
<FloatingPanel.Positioner>
<FloatingPanel.Content>
<FloatingPanel.DragTrigger>
<FloatingPanel.Header>
<FloatingPanel.Title>Floating Panel</FloatingPanel.Title>
<FloatingPanel.Control>
<FloatingPanel.StageTrigger stage="minimized">
<Minus />
</FloatingPanel.StageTrigger>
<FloatingPanel.StageTrigger stage="maximized">
<Maximize2 />
</FloatingPanel.StageTrigger>
<FloatingPanel.StageTrigger stage="default">
<ArrowDownLeft />
</FloatingPanel.StageTrigger>
<FloatingPanel.CloseTrigger>
<XIcon />
</FloatingPanel.CloseTrigger>
</FloatingPanel.Control>
</FloatingPanel.Header>
</FloatingPanel.DragTrigger>
<FloatingPanel.Body>
<p>Some content</p>
</FloatingPanel.Body>
<FloatingPanel.ResizeTrigger axis="n" />
<FloatingPanel.ResizeTrigger axis="e" />
<FloatingPanel.ResizeTrigger axis="w" />
<FloatingPanel.ResizeTrigger axis="s" />
<FloatingPanel.ResizeTrigger axis="ne" />
<FloatingPanel.ResizeTrigger axis="se" />
<FloatingPanel.ResizeTrigger axis="sw" />
<FloatingPanel.ResizeTrigger axis="nw" />
</FloatingPanel.Content>
</FloatingPanel.Positioner>
</Portal>
</FloatingPanel.Root>
)
}
import { FloatingPanel } from '@ark-ui/solid/floating-panel'
import { ArrowDownLeft, Maximize2, Minus, XIcon } from 'lucide-solid'
import { createSignal } from 'solid-js'
import { Portal } from 'solid-js/web'
export const ControlledOpen = () => {
const [open, setOpen] = createSignal(false)
return (
<FloatingPanel.Root open={open()} onOpenChange={(e) => setOpen(e.open)}>
<FloatingPanel.Trigger>Toggle Panel</FloatingPanel.Trigger>
<Portal>
<FloatingPanel.Positioner>
<FloatingPanel.Content>
<FloatingPanel.DragTrigger>
<FloatingPanel.Header>
<FloatingPanel.Title>Floating Panel</FloatingPanel.Title>
<FloatingPanel.Control>
<FloatingPanel.StageTrigger stage="minimized">
<Minus />
</FloatingPanel.StageTrigger>
<FloatingPanel.StageTrigger stage="maximized">
<Maximize2 />
</FloatingPanel.StageTrigger>
<FloatingPanel.StageTrigger stage="default">
<ArrowDownLeft />
</FloatingPanel.StageTrigger>
<FloatingPanel.CloseTrigger>
<XIcon />
</FloatingPanel.CloseTrigger>
</FloatingPanel.Control>
</FloatingPanel.Header>
</FloatingPanel.DragTrigger>
<FloatingPanel.Body>
<p>Some content</p>
</FloatingPanel.Body>
<FloatingPanel.ResizeTrigger axis="n" />
<FloatingPanel.ResizeTrigger axis="e" />
<FloatingPanel.ResizeTrigger axis="w" />
<FloatingPanel.ResizeTrigger axis="s" />
<FloatingPanel.ResizeTrigger axis="ne" />
<FloatingPanel.ResizeTrigger axis="se" />
<FloatingPanel.ResizeTrigger axis="sw" />
<FloatingPanel.ResizeTrigger axis="nw" />
</FloatingPanel.Content>
</FloatingPanel.Positioner>
</Portal>
</FloatingPanel.Root>
)
}
<script setup lang="ts">
import { FloatingPanel } from '@ark-ui/vue/floating-panel'
import { ArrowDownLeft, Maximize2, Minus, XIcon } from 'lucide-vue-next'
import { ref } from 'vue'
const open = ref(false)
</script>
<template>
<FloatingPanel.Root v-model:open="open">
<FloatingPanel.Trigger>Toggle Panel</FloatingPanel.Trigger>
<Teleport to="body">
<FloatingPanel.Positioner>
<FloatingPanel.Content>
<FloatingPanel.DragTrigger>
<FloatingPanel.Header>
<FloatingPanel.Title>Floating Panel</FloatingPanel.Title>
<FloatingPanel.Control>
<FloatingPanel.StageTrigger stage="minimized">
<Minus />
</FloatingPanel.StageTrigger>
<FloatingPanel.StageTrigger stage="maximized">
<Maximize2 />
</FloatingPanel.StageTrigger>
<FloatingPanel.StageTrigger stage="default">
<ArrowDownLeft />
</FloatingPanel.StageTrigger>
<FloatingPanel.CloseTrigger>
<XIcon />
</FloatingPanel.CloseTrigger>
</FloatingPanel.Control>
</FloatingPanel.Header>
</FloatingPanel.DragTrigger>
<FloatingPanel.Body>
<p>Some content</p>
</FloatingPanel.Body>
<FloatingPanel.ResizeTrigger axis="n" />
<FloatingPanel.ResizeTrigger axis="e" />
<FloatingPanel.ResizeTrigger axis="w" />
<FloatingPanel.ResizeTrigger axis="s" />
<FloatingPanel.ResizeTrigger axis="ne" />
<FloatingPanel.ResizeTrigger axis="se" />
<FloatingPanel.ResizeTrigger axis="sw" />
<FloatingPanel.ResizeTrigger axis="nw" />
</FloatingPanel.Content>
</FloatingPanel.Positioner>
</Teleport>
</FloatingPanel.Root>
</template>
Lazy mounting
To lazy mount the floating panel, you can pass the lazyMount prop to the machine.
import { FloatingPanel } from '@ark-ui/react/floating-panel'
import { Portal } from '@ark-ui/react/portal'
import { ArrowDownLeft, Maximize2, Minus, XIcon } from 'lucide-react'
export const LazyMount = () => (
<FloatingPanel.Root lazyMount onExitComplete={() => console.log('onExitComplete invoked')}>
<FloatingPanel.Trigger>Toggle Panel</FloatingPanel.Trigger>
<Portal>
<FloatingPanel.Positioner>
<FloatingPanel.Content>
<FloatingPanel.DragTrigger>
<FloatingPanel.Header>
<FloatingPanel.Title>Floating Panel</FloatingPanel.Title>
<FloatingPanel.Control>
<FloatingPanel.StageTrigger stage="minimized">
<Minus />
</FloatingPanel.StageTrigger>
<FloatingPanel.StageTrigger stage="maximized">
<Maximize2 />
</FloatingPanel.StageTrigger>
<FloatingPanel.StageTrigger stage="default">
<ArrowDownLeft />
</FloatingPanel.StageTrigger>
<FloatingPanel.CloseTrigger>
<XIcon />
</FloatingPanel.CloseTrigger>
</FloatingPanel.Control>
</FloatingPanel.Header>
</FloatingPanel.DragTrigger>
<FloatingPanel.Body>
<p>Some content</p>
</FloatingPanel.Body>
<FloatingPanel.ResizeTrigger axis="n" />
<FloatingPanel.ResizeTrigger axis="e" />
<FloatingPanel.ResizeTrigger axis="w" />
<FloatingPanel.ResizeTrigger axis="s" />
<FloatingPanel.ResizeTrigger axis="ne" />
<FloatingPanel.ResizeTrigger axis="se" />
<FloatingPanel.ResizeTrigger axis="sw" />
<FloatingPanel.ResizeTrigger axis="nw" />
</FloatingPanel.Content>
</FloatingPanel.Positioner>
</Portal>
</FloatingPanel.Root>
)
import { FloatingPanel } from '@ark-ui/solid/floating-panel'
import { ArrowDownLeft, Maximize2, Minus, XIcon } from 'lucide-solid'
import { Portal } from 'solid-js/web'
export const LazyMount = () => (
<FloatingPanel.Root lazyMount onExitComplete={() => console.log('onExitComplete invoked')}>
<FloatingPanel.Trigger>Toggle Panel</FloatingPanel.Trigger>
<Portal>
<FloatingPanel.Positioner>
<FloatingPanel.Content>
<FloatingPanel.DragTrigger>
<FloatingPanel.Header>
<FloatingPanel.Title>Floating Panel</FloatingPanel.Title>
<FloatingPanel.Control>
<FloatingPanel.StageTrigger stage="minimized">
<Minus />
</FloatingPanel.StageTrigger>
<FloatingPanel.StageTrigger stage="maximized">
<Maximize2 />
</FloatingPanel.StageTrigger>
<FloatingPanel.StageTrigger stage="default">
<ArrowDownLeft />
</FloatingPanel.StageTrigger>
<FloatingPanel.CloseTrigger>
<XIcon />
</FloatingPanel.CloseTrigger>
</FloatingPanel.Control>
</FloatingPanel.Header>
</FloatingPanel.DragTrigger>
<FloatingPanel.Body>
<p>Some content</p>
</FloatingPanel.Body>
<FloatingPanel.ResizeTrigger axis="n" />
<FloatingPanel.ResizeTrigger axis="e" />
<FloatingPanel.ResizeTrigger axis="w" />
<FloatingPanel.ResizeTrigger axis="s" />
<FloatingPanel.ResizeTrigger axis="ne" />
<FloatingPanel.ResizeTrigger axis="se" />
<FloatingPanel.ResizeTrigger axis="sw" />
<FloatingPanel.ResizeTrigger axis="nw" />
</FloatingPanel.Content>
</FloatingPanel.Positioner>
</Portal>
</FloatingPanel.Root>
)
<script setup lang="ts">
import { FloatingPanel } from '@ark-ui/vue/floating-panel'
import { ArrowDownLeft, Maximize2, Minus, XIcon } from 'lucide-vue-next'
</script>
<template>
<FloatingPanel.Root lazy-mount @exit-complete="() => console.log('onExitComplete invoked')">
<FloatingPanel.Trigger>Toggle Panel</FloatingPanel.Trigger>
<Teleport to="body">
<FloatingPanel.Positioner>
<FloatingPanel.Content>
<FloatingPanel.DragTrigger>
<FloatingPanel.Header>
<FloatingPanel.Title>Floating Panel</FloatingPanel.Title>
<FloatingPanel.Control>
<FloatingPanel.StageTrigger stage="minimized">
<Minus />
</FloatingPanel.StageTrigger>
<FloatingPanel.StageTrigger stage="maximized">
<Maximize2 />
</FloatingPanel.StageTrigger>
<FloatingPanel.StageTrigger stage="default">
<ArrowDownLeft />
</FloatingPanel.StageTrigger>
<FloatingPanel.CloseTrigger>
<XIcon />
</FloatingPanel.CloseTrigger>
</FloatingPanel.Control>
</FloatingPanel.Header>
</FloatingPanel.DragTrigger>
<FloatingPanel.Body>
<p>Some content</p>
</FloatingPanel.Body>
<FloatingPanel.ResizeTrigger axis="n" />
<FloatingPanel.ResizeTrigger axis="e" />
<FloatingPanel.ResizeTrigger axis="w" />
<FloatingPanel.ResizeTrigger axis="s" />
<FloatingPanel.ResizeTrigger axis="ne" />
<FloatingPanel.ResizeTrigger axis="se" />
<FloatingPanel.ResizeTrigger axis="sw" />
<FloatingPanel.ResizeTrigger axis="nw" />
</FloatingPanel.Content>
</FloatingPanel.Positioner>
</Teleport>
</FloatingPanel.Root>
</template>
Context
To access the context of the floating panel, you can use the useFloatingPanelContext hook or the
FloatingPanel.Context component.
import { FloatingPanel } from '@ark-ui/react/floating-panel'
import { Portal } from '@ark-ui/react/portal'
import { ArrowDownLeft, Maximize2, Minus, XIcon } from 'lucide-react'
export const RenderFn = () => (
<FloatingPanel.Root>
<FloatingPanel.Trigger>Toggle Panel</FloatingPanel.Trigger>
<FloatingPanel.Context>
{(floatingPanel) => <p>floatingPanel. is {floatingPanel.open ? 'open' : 'closed'}</p>}
</FloatingPanel.Context>
<Portal>
<FloatingPanel.Positioner>
<FloatingPanel.Content>
<FloatingPanel.DragTrigger>
<FloatingPanel.Header>
<FloatingPanel.Title>Floating Panel</FloatingPanel.Title>
<FloatingPanel.Control>
<FloatingPanel.StageTrigger stage="minimized">
<Minus />
</FloatingPanel.StageTrigger>
<FloatingPanel.StageTrigger stage="maximized">
<Maximize2 />
</FloatingPanel.StageTrigger>
<FloatingPanel.StageTrigger stage="default">
<ArrowDownLeft />
</FloatingPanel.StageTrigger>
<FloatingPanel.CloseTrigger>
<XIcon />
</FloatingPanel.CloseTrigger>
</FloatingPanel.Control>
</FloatingPanel.Header>
</FloatingPanel.DragTrigger>
<FloatingPanel.Body>
<p>Some content</p>
</FloatingPanel.Body>
<FloatingPanel.ResizeTrigger axis="n" />
<FloatingPanel.ResizeTrigger axis="e" />
<FloatingPanel.ResizeTrigger axis="w" />
<FloatingPanel.ResizeTrigger axis="s" />
<FloatingPanel.ResizeTrigger axis="ne" />
<FloatingPanel.ResizeTrigger axis="se" />
<FloatingPanel.ResizeTrigger axis="sw" />
<FloatingPanel.ResizeTrigger axis="nw" />
</FloatingPanel.Content>
</FloatingPanel.Positioner>
</Portal>
</FloatingPanel.Root>
)
import { FloatingPanel } from '@ark-ui/solid/floating-panel'
import { ArrowDownLeft, Maximize2, Minus, XIcon } from 'lucide-solid'
import { Portal } from 'solid-js/web'
export const RenderFn = () => (
<FloatingPanel.Root>
<FloatingPanel.Trigger>Toggle Panel</FloatingPanel.Trigger>
<FloatingPanel.Context>
{(floatingPanel) => <p>floatingPanel is {floatingPanel().open ? 'open' : 'closed'}</p>}
</FloatingPanel.Context>
<Portal>
<FloatingPanel.Positioner>
<FloatingPanel.Content>
<FloatingPanel.DragTrigger>
<FloatingPanel.Header>
<FloatingPanel.Title>Floating Panel</FloatingPanel.Title>
<FloatingPanel.Control>
<FloatingPanel.StageTrigger stage="minimized">
<Minus />
</FloatingPanel.StageTrigger>
<FloatingPanel.StageTrigger stage="maximized">
<Maximize2 />
</FloatingPanel.StageTrigger>
<FloatingPanel.StageTrigger stage="default">
<ArrowDownLeft />
</FloatingPanel.StageTrigger>
<FloatingPanel.CloseTrigger>
<XIcon />
</FloatingPanel.CloseTrigger>
</FloatingPanel.Control>
</FloatingPanel.Header>
</FloatingPanel.DragTrigger>
<FloatingPanel.Body>
<p>Some content</p>
</FloatingPanel.Body>
<FloatingPanel.ResizeTrigger axis="n" />
<FloatingPanel.ResizeTrigger axis="e" />
<FloatingPanel.ResizeTrigger axis="w" />
<FloatingPanel.ResizeTrigger axis="s" />
<FloatingPanel.ResizeTrigger axis="ne" />
<FloatingPanel.ResizeTrigger axis="se" />
<FloatingPanel.ResizeTrigger axis="sw" />
<FloatingPanel.ResizeTrigger axis="nw" />
</FloatingPanel.Content>
</FloatingPanel.Positioner>
</Portal>
</FloatingPanel.Root>
)
<script setup lang="ts">
import { FloatingPanel } from '@ark-ui/vue/floating-panel'
import { ArrowDownLeft, Maximize2, Minus, XIcon } from 'lucide-vue-next'
</script>
<template>
<FloatingPanel.Root>
<FloatingPanel.Context v-slot="floatingPanel">
<p>floatingPanel is {{ floatingPanel.open ? 'open' : 'closed' }}</p>
</FloatingPanel.Context>
<FloatingPanel.Trigger>Toggle Panel</FloatingPanel.Trigger>
<Teleport to="body">
<FloatingPanel.Positioner>
<FloatingPanel.Content>
<FloatingPanel.DragTrigger>
<FloatingPanel.Header>
<FloatingPanel.Title>Floating Panel</FloatingPanel.Title>
<FloatingPanel.Control>
<FloatingPanel.StageTrigger stage="minimized">
<Minus />
</FloatingPanel.StageTrigger>
<FloatingPanel.StageTrigger stage="maximized">
<Maximize2 />
</FloatingPanel.StageTrigger>
<FloatingPanel.StageTrigger stage="default">
<ArrowDownLeft />
</FloatingPanel.StageTrigger>
<FloatingPanel.CloseTrigger>
<XIcon />
</FloatingPanel.CloseTrigger>
</FloatingPanel.Control>
</FloatingPanel.Header>
</FloatingPanel.DragTrigger>
<FloatingPanel.Body>
<p>Some content</p>
</FloatingPanel.Body>
<FloatingPanel.ResizeTrigger axis="n" />
<FloatingPanel.ResizeTrigger axis="e" />
<FloatingPanel.ResizeTrigger axis="w" />
<FloatingPanel.ResizeTrigger axis="s" />
<FloatingPanel.ResizeTrigger axis="ne" />
<FloatingPanel.ResizeTrigger axis="se" />
<FloatingPanel.ResizeTrigger axis="sw" />
<FloatingPanel.ResizeTrigger axis="nw" />
</FloatingPanel.Content>
</FloatingPanel.Positioner>
</Teleport>
</FloatingPanel.Root>
</template>