Dock

Pro

A floating rail of icon controls that morphs, in place, into a labelled menu with drill-down layers. One box animates its own width, height and corner, so the rail becomes the menu instead of opening a second surface.

Installation#

# One-time setup (tokens + lib helpers)npx koalaui-cli@latest init# Activate your Pro license (once per machine)npx koalaui-cli@latest login <key># Add this component (its dependencies come along)npx koalaui-cli@latest add dock

Usage#

This example is part of Koala UI Pro

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

The label is a prop, not children, because the dock renders it twice: as the tooltip on the collapsed icon, and as the row's text in the panel. One source, two shapes.

Anatomy#

import {  Dock,  DockItem,  DockGroup,  DockLabel,  DockSeparator,  DockExpander,} from "@/components/ui/dock"<Dock>  <DockItem />  <DockSeparator />  <DockGroup>    <DockLabel />    <DockItem />  </DockGroup>  <DockExpander /></Dock>

Variants#

Floating is the default: the house's elevated popover surface, because a dock is chrome and chrome should inherit the page's theme rather than punch a slab of the opposite colour through it. Solid is the inverted slab, for a dock that has to hold its own over a photograph, a canvas or a video. It flips foreground and background rather than hardcoding black, so it reads dark on the light themes and light on the dark ones.

floating · collapsed
solid · collapsed
floating · expanded
solid · expanded

Sizes and density#

size sizes the rail, and only the rail. It is not the page's comfortable/compact knob, because a floating control bar that shrinks to form-compact becomes hard to hit. Every step is built on one identity, 4px padding + half the icon button = the rail's corner, so the buttons come out as true circles and concentric inside the pill at all three sizes. The 4px is the panel's own padding, so the rail and the menu it opens into read at one scale. The panel is a menu, so it follows density like every DropdownMenu on the page: the same 36px or 32px rows, row for row.

sm
md
lg
comfortable
compact

Layers#

A DockGroup is a drill-down. Opening it replaces the panel's contents with a “Back” row and its children, and the box resizes to fit: no second surface, no popup beside a popup. Groups nest, so a group inside a group is one layer deeper. Press Esc to step back one layer at a time.

Captions and shortcuts#

DockLabel sorts a long panel into topics, and shortcut puts the menu's gray key chip at the end of a row, exactly where DropdownMenuShortcut sits. Both live in the panel only: the rail has no room for a word, so while the dock is collapsed the shortcut rides in the tooltip beside the label instead.

Trigger#

trigger="click" is the default and the right pick on touch. trigger="hover" expands when the pointer lands on the expander and collapses shortly after it leaves the dock. The grace period is there so crossing a 2px gap on the way to a row never dismisses the thing you are reaching for.

trigger="click"
trigger="hover"

States#

active marks the current destination in ink, not colour: a faint chip, a heavier label and a full-strength glyph among muted ones (it also sets aria-current="page"). disabled takes a control out of the roving focus, and DockSeparator is axis-aware: a vertical tick in the rail, a horizontal rule in the panel.

Pass asChild with your own element and the dock renders its icon and label inside it, so a nav dock can use next/link and keep client-side routing.

Controlled#

Drive the shape yourself with expanded / onExpandedChange, to open the dock from a keyboard shortcut, or to keep it open while a tour points at it.

The dock is collapsed.

API reference#

Dock

The morphing box and roving-focus root, Radix Toolbar.Root (a <div role="toolbar">); forwards all its props except orientation, which follows the shape. Every part accepts className, merged last.

PropTypeDefaultDescription
aria-labelstring"Dock"Accessible name of the toolbar.
variant"floating" | "solid""floating"The page's popover surface, or an inverted slab for a dock over a photo, canvas or video.
size"sm" | "md" | "lg""md"The rail only: 24px, 32px or 40px icon buttons. The panel ignores it.
density"comfortable" | "compact""compact"The panel only: 36px or 32px rows, like DropdownMenu. Falls back to the nearest DensityProvider.
expandedboolean-Controlled shape: true is the panel, false the rail.
defaultExpandedbooleanfalseShape on mount when uncontrolled.
onExpandedChange(expanded: boolean) => void-Called when the dock expands or collapses.
trigger"click" | "hover""click"What opens the panel: a press on DockExpander, or the pointer landing on it (then it collapses shortly after the pointer leaves).
closeOnSelectbooleantrueCollapse back to the rail after a DockItem is chosen.
tooltipPlacement"top" | "bottom" | "left" | "right""top"Where the rail icons' tooltips open; also takes Tippy's -start, -end and auto placements.
staticbooleanfalseKept for compatibility; controls no longer scale on press.
loopbooleantrueArrow keys wrap from the last control to the first.
dir"ltr" | "rtl"-Reading direction for arrow-key movement.
Data attributeDescription
data-expandedPresent while the dock is in its panel shape.
data-readyPresent once the box has measured itself; the morph transition is armed only then.
data-orientation"horizontal" in the rail, "vertical" in the panel.
CSS variableDescription
--surfaceSet to var(--popover) (floating) or var(--foreground) (solid), read by nested controls.

DockItem

A destination or action, Radix Toolbar.Button: an icon with the label as its tooltip in the rail, a labelled row in the panel. Forwards no other native props.

PropTypeDefaultDescription
label*string-The tooltip and aria-label in the rail, the row text in the panel.
iconReact.ReactNode-The glyph; sized by size in the rail and to 20px in the panel unless it carries a size-* class.
shortcutstring-Display-only key chip at the end of the row; rides in the tooltip while collapsed.
activebooleanfalseMarks the current destination and sets aria-current="page".
disabledbooleanfalseDims the control and removes it from the roving focus.
onSelect(event: React.MouseEvent<HTMLElement>) => void-Called on press, before closeOnSelect collapses the dock.
asChildbooleanfalseRender into your single child (a Link, an <a>); the dock still fills it with the icon and label.
Data attributeDescription
data-activePresent on the current destination.
data-orientationThe dock's current arrow-key axis.

DockGroup

A drill-down layer: an icon in the rail, a row with a caret in the panel. Opening it swaps the panel for a Back row plus its children; nest groups to go deeper.

PropTypeDefaultDescription
label*string-Tooltip in the rail, row text in the panel.
iconReact.ReactNode-The glyph.
valuestring-Identity in the open path; defaults to label.
backLabelstring"Back"Label of the row that returns to the parent layer.
disabledbooleanfalseDims the control and removes it from the roving focus.
childrenReact.ReactNode-The rows of this group's layer.

DockLabel

A muted caption over a run of rows, rendered only in the panel. Forwards all native <div> props.

DockSeparator

An axis-aware hairline, Radix Toolbar.Separator: a vertical tick in the rail, a horizontal rule in the panel. Forwards all its props.

Data attributeDescription
data-orientationThe separator's own axis, opposite the dock's.

DockExpander

The rail's last control, which turns the rail into the panel; it unmounts once expanded.

PropTypeDefaultDescription
labelstring"Menu"Accessible name and tooltip.
childrenReact.ReactNode-Replaces the default list glyph.

FAQ#

Toolbar is a band of controls: it stays one shape and groups icon buttons, toggles and dropdown triggers. Dock is a navigation surface that changes shape: it collapses to a rail of icons and expands, in place, into a labelled menu with drill-down layers. Both are built on Radix Toolbar, so they share the roving focus and arrow-key model; reach for Toolbar to act on something (a format bar, a tool rail) and for Dock to go somewhere.