npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

overflow-toolbar

v0.2.4

Published

Responsive toolbar overflow component — items automatically collapse into a dropdown menu as the container shrinks. Supports visible, icon-only (min), and hidden states. Ships with React (Radix UI), MUI, and vanilla JS implementations.

Readme

Overflow

npm version bundle size license CI

A responsive toolbar overflow component that automatically collapses items into a dropdown menu as the container shrinks. Items transition through three states: visiblemin (icon-only) → hidden (moved to menu).

Features

  • Automatic overflow detection — items collapse into a menu as the container narrows using ResizeObserver
  • Three item states — visible, min (icon-only), and hidden (in menu)
  • Compact mode — items collapse one at a time instead of all at once
  • Reverse mode — collapse from the right instead of the left
  • Menu-only items — items that always live in the dropdown (e.g. Help, About)
  • Min state — items shrink to icon-only before being fully hidden
  • Three implementations — React/Radix UI (shadcn-compatible), Material UI, and vanilla JavaScript
  • Tree-shakeable — import only the variant you need via subpath exports
  • TypeScript — full type declarations included

Install

npm install overflow-toolbar

or

pnpm add overflow-toolbar

or

yarn add overflow-toolbar

Quick Start

Radix UI / shadcn (React)

import { RxOverflow, RxOverflowItem, RxOverflowMenu } from 'overflow-toolbar/rx';

<RxOverflow style={{ gap: 8 }}>
  <RxOverflowMenu opener={<button>More</button>}>
    <RxOverflowItem menuId="format"><button>Format</button></RxOverflowItem>
    <RxOverflowItem menuId="filter"><button>Filters</button></RxOverflowItem>
  </RxOverflowMenu>

  <RxOverflowItem menuId="format"><button>Format</button></RxOverflowItem>
  <RxOverflowItem menuId="filter"><button>Filters</button></RxOverflowItem>
</RxOverflow>

Material UI (React)

import { MuiOverflow, MuiOverflowItem, MuiOverflowMenu } from 'overflow-toolbar/mui';
import { Button, MenuItem } from '@mui/material';

<MuiOverflow sx={{ gap: 1 }}>
  <MuiOverflowMenu opener={<Button>More</Button>}>
    <MuiOverflowItem menuId="format"><MenuItem>Format</MenuItem></MuiOverflowItem>
    <MuiOverflowItem menuId="filter"><MenuItem>Filters</MenuItem></MuiOverflowItem>
  </MuiOverflowMenu>

  <MuiOverflowItem menuId="format"><Button>Format</Button></MuiOverflowItem>
  <MuiOverflowItem menuId="filter"><Button>Filters</Button></MuiOverflowItem>
</MuiOverflow>

Vanilla JavaScript

import { OverflowToolbar } from 'overflow-toolbar/vanilla';

const ul = document.querySelector('#my-toolbar');
const toolbar = new OverflowToolbar(ul);

// Later: toolbar.update() after DOM changes
// Cleanup: toolbar.destroy()
<ul id="my-toolbar">
  <li data-overflow-role="menu">
    <button data-menu-trigger>More</button>
    <div data-menu-panel>
      <button data-menu-id="format">Format</button>
      <button data-menu-id="filter">Filters</button>
    </div>
  </li>
  <li data-overflow-role="item" data-menu-id="format">
    <button>Format</button>
  </li>
  <li data-overflow-role="item" data-menu-id="filter">
    <button>Filters</button>
  </li>
</ul>

Subpath Imports

Import only what you need for optimal tree-shaking:

| Import path | Contents | |---|---| | overflow-toolbar | Everything (all variants) | | overflow-toolbar/core | Core React components (Overflow, OverflowItem, OverflowMenu, OverflowController) | | overflow-toolbar/rx | Radix UI / shadcn variant (RxOverflow, RxOverflowItem, RxOverflowMenu) | | overflow-toolbar/mui | Material UI variant (MuiOverflow, MuiOverflowItem, MuiOverflowMenu) | | overflow-toolbar/vanilla | Vanilla JS (OverflowToolbar) |

CSS is included automatically when you import any subpath — no separate style imports needed.

API

Overflow / RxOverflow / MuiOverflow

The container component. Wraps toolbar items and the overflow menu.

| Prop | Type | Default | Description | |---|---|---|---| | children | ReactNode | — | Overflow items and menu | | compact | boolean | false | Collapse items one at a time | | reverse | boolean | false | Collapse from the right | | className | string | — | CSS class name | | style | CSSProperties | — | Inline styles | | sx | SxProps | — | MUI system props (MUI only) |

OverflowItem / RxOverflowItem / MuiOverflowItem

Wraps each toolbar item. Place matching items in both the toolbar and the menu, linked by menuId.

| Prop | Type | Default | Description | |---|---|---|---| | children | ReactNode | — | Item content | | menuId | string | — | Links toolbar item to its menu counterpart | | minStateWidth | string | number | — | Width in min state (string for Rx, number for MUI spacing units) |

OverflowMenu / RxOverflowMenu / MuiOverflowMenu

The dropdown menu container. Must include an opener element (the trigger button).

| Prop | Type | Default | Description | |---|---|---|---| | children | ReactNode | — | Menu items | | opener | ReactNode | — | The button that opens the menu | | open | boolean | — | Controlled open state. When provided, you manage open/close. | | onOpenChange | (open: boolean) => void | — | Called when the menu opens or closes. Use alone for notifications, or with open for full control. |

Controlled Menu

By default, the menu manages its own open/close state. To control it programmatically, pass open and onOpenChange:

const [menuOpen, setMenuOpen] = useState(false);

<RxOverflowMenu
  opener={<button>More</button>}
  open={menuOpen}
  onOpenChange={setMenuOpen}
>
  {/* items */}
</RxOverflowMenu>

// Close programmatically from anywhere:
setMenuOpen(false);

OverflowController

Framework-agnostic controller class for building custom implementations.

| Method | Description | |---|---| | connect() | Start observing and apply initial state | | disconnect() | Stop observing and clear all applied styles/attributes | | update() | Re-scan children and restart (call after DOM changes) |

Important: No Wrapper Elements

OverflowItem and OverflowMenu components must be direct children of the Overflow container. Do not wrap them in <div>, <span>, layout components, or any other intermediate elements.

The overflow engine scans immediate children for internal role markers to measure, track, and transition items between states. Wrapper elements break this scan, causing items to never collapse, never appear in the menu, or fail to transition between visible/min/hidden states.

{/* WRONG — wrapper breaks overflow behavior */}
<RxOverflow>
  <div className="group">
    <RxOverflowItem menuId="a"><button>A</button></RxOverflowItem>
    <RxOverflowItem menuId="b"><button>B</button></RxOverflowItem>
  </div>
</RxOverflow>

{/* CORRECT — items are direct children */}
<RxOverflow>
  <RxOverflowItem menuId="a"><button>A</button></RxOverflowItem>
  <RxOverflowItem menuId="b"><button>B</button></RxOverflowItem>
</RxOverflow>

The same rule applies to the vanilla JS variant — <li> elements with data-overflow-role must be direct children of the <ul> container.

Modes

Compact Mode

Items collapse one at a time with tight spacing. Adjacent buttons get squared-off corners for a grouped look:

<RxOverflow compact>
  {/* items... */}
</RxOverflow>

Reverse Mode

Items collapse from the right side first instead of the left:

<RxOverflow reverse>
  {/* items... */}
</RxOverflow>

Min State

Items shrink to a fixed width (icon-only) before being fully hidden:

<RxOverflowItem menuId="format" minStateWidth="2.25rem">
  <button><FormatIcon /> Format</button>
</RxOverflowItem>

For MUI, minStateWidth accepts a number (theme spacing units):

<MuiOverflowItem menuId="format" minStateWidth={5}>
  <Button><FormatIcon /> Format</Button>
</MuiOverflowItem>

Menu-Only Items

Items without a menuId always stay where they are — in the toolbar or in the menu:

<RxOverflowMenu opener={<button>More</button>}>
  <RxOverflowItem menuId="format"><button>Format</button></RxOverflowItem>
  {/* Always in the menu */}
  <RxOverflowItem><div role="separator" /></RxOverflowItem>
  <RxOverflowItem><button>Help</button></RxOverflowItem>
</RxOverflowMenu>

Vanilla JS

The vanilla implementation uses data- attributes for configuration:

| Attribute | Element | Description | |---|---|---| | data-overflow-role="item" | <li> | Marks a toolbar item | | data-overflow-role="menu" | <li> | Marks the menu container | | data-menu-id | <li>, menu item | Links toolbar and menu items | | data-min-state-width | <li> | Min-state width (CSS value) | | data-menu-trigger | <button> | The menu open/close button | | data-menu-panel | <div> | The menu panel (uses Popover API) |

const toolbar = new OverflowToolbar(document.querySelector('ul'), {
  compact: true,
  reverse: false,
});

toolbar.update();   // after DOM changes
toolbar.destroy();  // cleanup

Browser Support

Requires ResizeObserver (all modern browsers). The vanilla JS variant also uses the Popover API for the dropdown menu.

Contributing

git clone https://github.com/wesjones/overflow.git
cd overflow
pnpm install
pnpm storybook     # dev with Storybook on port 6006
pnpm test:unit     # run unit tests
pnpm build:lib     # build the library
pnpm typecheck     # check types
pnpm lint           # lint

Also Known As

This component implements what is commonly known as a responsive toolbar, overflow menu, adaptive toolbar, collapsible toolbar, priority+ pattern, priority-plus navigation, toolbar button group overflow, responsive action bar, action bar, action buttons, overflow actions, button rollup, button rollbar, command bar, or "more menu." It handles responsive buttons, auto-collapse, icon-only collapse, and dynamic toolbar resizing using ResizeObserver. The Radix UI variant is fully compatible with shadcn/ui projects.

License

MIT