JSPM

react-overflow-tabs

1.0.0
  • ESM via JSPM
  • ES Module Entrypoint
  • Export Map
  • Keywords
  • License
  • Repository URL
  • TypeScript Types
  • README
  • Created
  • Published
  • Downloads 6
  • Score
    100M100P100Q31475F
  • License MIT

A lightweight React hook for responsive tab navigation with automatic overflow handling. Works with any UI framework (Tailwind, Bootstrap, MUI, custom CSS).

Package Exports

  • react-overflow-tabs
  • react-overflow-tabs/package.json

Readme

react-overflow-tabs

A headless React hook to gracefully collapse overflowing tabs into a dropdown menu.
Works with any UI framework (Tailwind, Bootstrap, MUI, custom CSS).

npm version
license

A lightweight React hook for responsive tab navigation with automatic overflow handling.
Perfect for building tab systems, breadcrumbs, or step navigators.


🎥 Demo

Demo of react-overflow-tabs


✨ Features

  • 🔗 Headless – no styles, works with your own UI
  • 📏 Responsive – detects tab overflow using IntersectionObserver
  • Lightweight – tree-shakeable, no dependencies
  • 🎯 Framework-agnostic – use with Tailwind, Bootstrap, MUI, or custom CSS

📦 Installation

npm install react-overflow-tabs
# or
yarn add react-overflow-tabs
# or
pnpm add react-overflow-tabs

Peer dependencies:

  • react >=18 <20

🚀 Quickstart

⚠️ Do not use display: none or move elements out of flow with position: absolute; Both will cause IntersectionObserver to stop tracking the element. Instead:

  • Use visibility: hidden
import { useRef } from "react";
import { useOverflowTabs } from "react-overflow-tabs";

const Example = () => {
    const containerRef = useRef<HTMLDivElement>(null);

    const { overflowKeys, isOverflowing } = useOverflowTabs({
        container: containerRef,
    });

    const KEYS = [
        "home-1",
        "profile-1",
        "settings-1",
        "home-2",
        "profile-2",
        "settings-2",
        "home-3",
        "profile-3",
        "settings-3",
        "home-4",
        "profile-4",
        "settings-4",
    ];

    return (
        <div
            ref={containerRef}
            style={{
                display: "flex",
                gap: 12,
                overflow: "hidden",
            }}
        >
            {KEYS.map((k) => (
                <button
                    key={k}
                    data-overflow-key={k}
                    style={{
                        whiteSpace: "nowrap",
                        visibility: overflowKeys.includes(k) ? "hidden" : "visible",
                    }}
                >
                    {k}
                </button>
            ))}
            {isOverflowing && (
                <div style={{ marginLeft: "auto" }}>
                    <div
                        style={{
                            position: "absolute",
                            marginTop: 40,
                            right: 4,
                            border: "1px solid red",
                            padding: 6,
                        }}
                    >
                        {overflowKeys.map((key) => (
                            <div key={key} style={{ padding: "6px 10px", whiteSpace: "nowrap" }}>
                                {key}
                            </div>
                        ))}
                    </div>
                </div>
            )}
        </div>
    );
};

⚙️ API

useOverflowTabs(options: IOverflowTabsOptions): IOverflowState

Options (IOverflowTabsOptions)

Option Type Default Description
container RefObject<HTMLElement> | HTMLElement required The container element to observe.
tabSelector string "[data-overflow-key]" Selector for tab elements. Each tab must have a unique data-overflow-key.
disabled boolean false Temporarily disable overflow tracking.

Return (IOverflowState)

Key Type Description
visibleKeys string[] Keys of tabs currently visible in the container.
overflowKeys string[] Keys of tabs pushed into overflow (hidden).
isOverflowing boolean Whether overflow is currently happening.

🛠 Example with Tailwind

import { useRef } from "react";
import useOverflowTabs from "./useOverflowTabs";

const Example = () => {
    const containerRef = useRef<HTMLDivElement>(null);

    const { overflowKeys, isOverflowing } = useOverflowTabs({
        container: containerRef,
    });

    const KEYS = [
        "home-1",
        "profile-1",
        "settings-1",
        "home-2",
        "profile-2",
        "settings-2",
        "home-3",
        "profile-3",
        "settings-3",
        "home-4",
        "profile-4",
        "settings-4",
    ];

    return (
        <div ref={containerRef} className="flex gap-3 overflow-hidden">
            {KEYS.map((k) => (
                <button
                    key={k}
                    data-overflow-key={k}
                    className={"p-2 whitespace-nowrap border" + (overflowKeys.includes(k) ? " invisible" : "")}
                >
                    {k}
                </button>
            ))}

            {isOverflowing && (
                <div className="ml-auto">
                    <div className="absolute right-4 mt-14 border">
                        {overflowKeys.map((key) => (
                            <div key={key} className="p-2 whitespace-nowrap">
                                {key}
                            </div>
                        ))}
                    </div>
                </div>
            )}
        </div>
    );
};

📖 Notes

  • Uses IntersectionObserver under the hood → supported in modern browsers.
  • threshold: 0.999 by default → requires full visibility to count as visible.
  • Works with RTL layouts, as it only tracks visibility.

🤝 Contributing

Contributions, issues, and feature requests are welcome!
Feel free to open an issue or submit a PR.

  1. Fork the repo
  2. Create your feature branch (git checkout -b feature/my-feature)
  3. Commit your changes (git commit -m 'Add some feature')
  4. Push to the branch (git push origin feature/my-feature)
  5. Open a Pull Request

📜 License

MIT © Baver Bozdağ