Floating Window
ProA movable, resizable window over your app, the way a desktop keeps a second document at hand. Drag it by its title bar, resize it from the corner, double-click the bar to fold it away. Geometry is in rem, so it scales with the root font size, and the app keeps the state.
Installation#
Usage#
The preview is yours to explore. Its code comes with a Pro license, paid once and used in every project.
Have a license? Unlock it here
Anatomy#
import { FloatingWindow, FloatingWindowLayer } from "@/components/ui/floating-window"
<FloatingWindowLayer>
<FloatingWindow title="…" x={…} y={…} width={…} height={…}>
…
</FloatingWindow>
</FloatingWindowLayer>Managing windows#
The window is controlled: it reports a drag through onMove(x, y) and a resize through onResize(width, height), already kept inside the layer and above the minimum, and draws whatever you pass back. The z-order is yours too: give each window a zIndex and raise it in onFocusWindow, which fires when the window is pressed anywhere or focus moves into it from outside. A few lines of state are the whole window manager.
The preview is yours to explore. Its code comes with a Pro license, paid once and used in every project.
Have a license? Unlock it here
Minimize and controls#
minimized folds the window up to its title bar, like a window shade. Its content stays mounted (a half-written note survives) and turns inert until it unfolds. The bar shows a control only for a handler you pass: onToggleMinimize adds the minimize button and the double-click on the bar, onClose adds the close button. Double-click the folded bar below.
Sizing in rem#
x, y, width, height, minWidth (18 by default) and minHeight (4.25: the bar and 2rem of content) are all rem, so a UI that scales by changing the root font size scales its windows with it. The pointer is converted with the root font size read when a drag starts. A move keeps 6rem of the window inside the layer on the left (the controls) and 4rem on the right, and the bar never leaves the top or bottom; a resize stops half a rem short of the layer's far edges. Without a FloatingWindowLayer around it, a window is unbounded.
API reference#
FloatingWindowLayer
An absolute inset-0 <div> over its positioned parent, transparent to the pointer except where a window is. Its box bounds the windows inside it; a FloatingWindow outside a layer still works, without bounds. Every div prop is forwarded, ref included; className is merged last.
| Data attribute | Description |
|---|---|
| data-slot | "floating-window-layer". |
FloatingWindow
A non-modal role="dialog" named by its title (or your aria-label). Controlled: it reports moves and resizes and the app keeps the state, z-order included. Every other div prop is forwarded; style merges over the geometry and className is merged last.
| Prop | Type | Default | Description |
|---|---|---|---|
| title* | React.ReactNode | - | The window's name: shown in the bar, and the dialog's accessible name. |
| icon | React.ElementType | - | A glyph before the title: the component, not an element (icon={Browser}). |
| x* | number | - | Left edge inside the layer, in rem. |
| y* | number | - | Top edge inside the layer, in rem. |
| width* | number | - | Width in rem. |
| height* | number | - | Height in rem, while not minimized. |
| minimized | boolean | false | Folded to the title bar. The content stays mounted (and inert). |
| zIndex | number | - | Stacking order among the layer's windows. Raise it in onFocusWindow. |
| minWidth | number | 18 | Smallest width a resize can reach, in rem. |
| minHeight | number | 4.25 | Smallest height a resize can reach, in rem: the bar and 2rem of content. |
| onFocusWindow | () => void | - | The window was pressed, or focus moved into it: bring it to the front. |
| onMove | (x: number, y: number) => void | - | A drag of the title bar: the new x and y, in rem, already kept inside the layer. |
| onResize | (width: number, height: number) => void | - | A drag of the corner grip: the new width and height, in rem, within the limits. |
| onToggleMinimize | () => void | - | Shows the minimize button and enables double-click on the bar. |
| onClose | () => void | - | Shows the close button. |
| minimizeLabel | string | "Minimize" | The minimize button's accessible name. |
| closeLabel | string | "Close" | The close button's accessible name. |
| static | boolean | false | Kept for compatibility; controls no longer scale on press. |
| Data attribute | Description |
|---|---|
| data-slot | "floating-window", with floating-window-bar, -title, -controls, -minimize, -close, -body and -grip inside. |
| data-state | "open" or "minimized", on the window and on its body. |
| CSS variable | Description |
|---|---|
| --surface | Set to var(--popover) on the body, the ground its controls paint on. |