Window

The window frame: corner radius, shadow, traffic lights, the inactive state, dragging, resizing, and minimizing into the Dock.

@cupertinoui/windowRegistry JSON

Installation

pnpm dlx shadcn@latest add @cupertinoui/window

The first install also adds the macOS theme. See Installation to set up the registry.

Usage

import {
  Window,
  WindowContent,
  WindowControls,
  WindowMain,
  WindowTitle,
  WindowTitlebar,
} from "@/components/ui/window"

<Window>
  <WindowMain>
    <WindowTitlebar>
      <WindowControls />
      <WindowTitle>Untitled</WindowTitle>
    </WindowTitlebar>
    <WindowContent>…</WindowContent>
  </WindowMain>
</Window>
A draggable window can be grabbed by its WindowTitlebar, Toolbar or MacSidebarHeader. Add data-window-drag-region to make any other element a handle, and data-window-no-drag to keep a custom control inside a handle clickable.
Mount the Dock tile a window minimizes into before you set minimized, so the animation knows where to go. The minimize and zoom animations are skipped when the user prefers reduced motion.
The resize handles are thin strips along the inside of the frame. Resizing keeps the opposite edges in place, even for a window centered by its classes, by adjusting the same offset that dragging uses. A zoomed window can't be resized.

Examples

Inactive

Unfocused windows have grey traffic lights, a dimmed title and turn their accent color grey.

Draggable

With draggable, the window moves when you drag its title bar, toolbar or the strip above its sidebar. Buttons, fields and other controls in those areas keep working. bounds="parent" keeps the window inside its parent.

Minimize and zoom

Give the window onClose, onMinimize and onZoom and the traffic lights work. minimized plays the Genie effect into the minimizeTo element, here the app's Dock icon, and back out again. maximized zooms the window to fill its parent, minus maximizeInsets; double-clicking the title bar zooms too.

Resizable

With resizable, the edges and corners resize the window, between minSize and maxSize. Double-click an edge to stretch it to the edge of the parent; hold Option to stretch the opposite edge too. Here the size is controlled and shown in the window.

API Reference

Every part forwards the props of the underlying element. The props below are the ones CupertinoUI adds or changes.

Window

PropTypeDescription
inactiveboolean
Default: false
Renders the unfocused look.
draggableboolean
Default: false
Lets people move the window by its title bar, toolbar or sidebar header. The offset is applied with the CSS translate property, so it works with any positioning.
bounds"parent"Keeps a dragged or resized window inside its parent element.
defaultPosition{ x: number; y: number }
Default: { x: 0, y: 0 }
The initial offset of an uncontrolled draggable window.
position{ x: number; y: number }The offset of a controlled window. Update it from onPositionChange.
onPositionChange(position: { x: number; y: number }) => voidCalled while the window is dragged, and when resizing moves its top or left edge.
resizableboolean
Default: false
Lets people resize the window by its edges and corners.
defaultSize{ width: number; height: number }The initial size of an uncontrolled window. Without it, the window keeps the size its classes give it until it is resized.
size{ width: number; height: number }The size of a controlled window. Update it from onSizeChange.
onSizeChange(size: { width: number; height: number }) => voidCalled while the window is resized.
minSize{ width?: number; height?: number }
Default: { width: 240, height: 140 }
The smallest size the window can be resized to.
maxSize{ width?: number; height?: number }The largest size. Give minSize and maxSize the same width (or height) to fix it; only the other edges resize then.
onClose / onMinimize / onZoom() => voidHandlers for the red, yellow and green traffic lights of the window's WindowControls. onZoom also runs when the title bar or toolbar is double-clicked.
minimizedboolean
Default: false
Animates the window into minimizeTo and hides it; set it back to false to animate it out again.
minimizeToRefObject<Element | null>The element the window minimizes into, such as a DockItem. Without it the window shrinks toward the bottom of the screen.
minimizeEffect"genie" | "scale"
Default: "genie"
The Genie effect, or a plain scale.
maximizedboolean
Default: false
Zooms the window to fill its positioned parent, animating between the two frames.
maximizeInsets{ top?, right?, bottom?, left?: number }Space to leave around a zoomed window, e.g. for a menu bar or Dock.
classNamestringExtra classes, merged with the component's own.

WindowControls

PropTypeDescription
onClose / onMinimize / onZoom() => voidHandlers for the traffic lights. By default they call the handlers of the Window.

WindowMain

Stacks the toolbar or title bar above the content, to the right of a sidebar.

PropTypeDescription
classNamestringExtra classes, merged with the component's own.

WindowTitlebar / WindowTitle / WindowContent

PropTypeDescription
classNamestringExtra classes, merged with the component's own.