Yugen UI

Overflow

Overflow moves items that don't fit the available width into a "…" menu button, in priority order.

Wrap the container (Toolbar, TabsList, Breadcrumb, TagGroup or a flex div) in <Overflow> and each item in <OverflowItem id priority>. Sizes are measured with ResizeObserver and cached. Hidden items stay mounted but get hidden + data-overflowing, so they can't receive focus. The menu button uses useOverflowMenu() (ref + number of hidden items) and useOverflowItems() (ids of the hidden items, in order) to render the matching MenuItems. The first render (and SSR) always shows every item. RTL works because only widths are measured, with no left/right assumptions.
Container width (%)100

Import

Install from npm, copy the source into your project with @yugen-ui/cli or the shadcn CLI, or import directly when working in this repo.

TSX
// npm install yugen-uiimport { Overflow, OverflowItem, OverflowDivider, useOverflowMenu, useOverflowItems } from "yugen-ui";

Variants

Each tile is a standalone example: hover to copy, or press Code to see the source.

Priority & minimumVisible

Higher priority stays visible longer: edit (3) → share (2) → delete (1) → the rest. minimumVisible={1} keeps at least one button even in a very narrow container.

Tabs

The selected tab has priority 1, so it is always visible; picking a tab from the menu brings it back out. The menu button sits outside TabsList (a tablist should only contain tabs).

Breadcrumb & OverflowDivider

Each item has its own groupId, and OverflowDivider hides with its group. The first item and the current page have priority 1; the middle items collapse into "…". Drag the corner of the frame to change its width.

ReactTypeScriptTailwind CSSRadix UIFluent 2ViteVitestPlaywrightNode.js

TagGroup "+N"

Tags that don't fit on one line collapse into a "+N" InteractionTag that opens a menu.

Usage

Do
  • Only render the menu button when isOverflowing, and attach the ref from useOverflowMenu() to its outermost element.
  • Give important or selected items (active tab, current page) a high priority so they don't get hidden.
  • Give the container a width that doesn't depend on its content (block, w-full, flex-1). Overflow adds w-full min-w-0 itself.
Don't
  • Don't keep the menu button visible all the time. Overflow only reserves space for it when items are hidden.
  • Don't use Overflow for readable paragraph content. Truncate or wrap the text instead.
  • Don't put items with a flexible width (flex-1) inside: each item must have its natural size.

Props

Prop · TypeDefaultDescription
Overflow.childrenReactElementDefault: —A single container that forwards its ref and accepts className.
overflowAxis"horizontal" | "vertical"Default: "horizontal"Measured axis (vertical needs a container with a fixed height).
overflowDirection"end" | "start"Default: "end"Among items with the same priority, hide from the end or from the start.
minimumVisiblenumberDefault: 0Number of items that always stay visible.
paddingnumberDefault: 0Extra space (px) reserved at the end; the menu button is already measured.
onOverflowChange({ hiddenIds, visibleIds }) => voidDefault: —Called when the set of hidden items changes.
OverflowItem.id / priority / groupIdstring / number / stringDefault: — / 0 / —Identifier, priority (higher stays longer), group.
OverflowDivider.groupIdstringDefault: —The divider hides when every item in its group is hidden (no ref-forwarding child needed).
useOverflowMenu<T>(){ ref, overflowCount, isOverflowing }Default: —For the "…" menu button.
useOverflowItems()string[]Default: —Ids of the hidden items, in DOM order.
useIsOverflowItemVisible(id) / useIsOverflowGroupVisible(groupId)boolean / "visible" | "overflow" | "hidden"Default: —State of one item / one group.
computeOverflow(input)(input) => { visibleIds, hiddenIds }Default: —The pure algorithm (no DOM), for tests or custom layouts.