react-laag
Headless React hooks for positioning tooltips, dropdown menus, and popovers.
Repository Health
Technical Analysis
react-laag is a headless React library that handles the hard part of overlay UI: figuring out where a tooltip, dropdown, or popover should appear relative to its trigger, and keeping it correctly placed as the user scrolls or the viewport changes. It does the positioning math and leaves styling, animation, and markup entirely up to you.
Built around the useLayer hook, react-laag renders overlays into a portal, flips and shifts them to stay inside the viewport, and reports the chosen placement plus arrow coordinates. With no runtime dependencies beyond a tiny warning helper and an 8kb tree-shakable footprint, it is a lightweight foundation for design systems and custom select, autocomplete, and menu components.
What You Get
- A useLayer hook that positions overlays and reports the resolved placement
- Automatic flipping and shifting to keep layers inside the viewport or a scroll container
- A useHover hook and Arrow component for building tooltips with pointer arrows
- Portal rendering so overlays escape overflow and stacking-context traps
- A tiny, dependency-free, tree-shakable bundle with full TypeScript types
Common Use Cases
- Positioning tooltips and popovers in a custom design system
- Building dropdown menus and context menus that stay on screen
- Anchoring autocomplete and select result lists to an input
- Rendering hover cards that follow their trigger while scrolling
Under The Hood
Architecture - The library lives in packages/react-laag/src, with useLayer.ts as the central hook that wires together the trigger and layer elements. Placement logic is modelled explicitly: Placement.ts, Placements.ts, and PlacementType.ts enumerate candidate positions, Bounds.ts and BoundsOffsets.ts handle rectangle math, and SubjectsBounds.ts aggregates the measured elements. useTrackElements.ts observes scroll and resize to recompute positions, useHover.ts drives tooltip timing, and Arrow.ts / getArrowStyle.ts compute arrow placement. Overlays are rendered through a portal so they escape overflow and stacking contexts.
Tech Stack - Written in TypeScript with only tiny-warning as a runtime dependency and react/react-dom (16-18) as peers. It builds with Rollup into CommonJS and ESM outputs, enforces a size budget via size-limit, and includes an SSR smoke test. The repository is a Yarn-workspaces monorepo that also houses a Storybook and a documentation website.
Code Quality - The packages/react-laag/tests directory holds focused unit specs for the geometry modules (Bounds, Placement, BoundsOffsets, SubjectsBounds) plus browser-based integration specs run with @web/test-runner and Puppeteer covering overflow-container and general positioning behavior. Types are centralised in types.ts, and the code is split into small, single-responsibility modules.
API Design - The public API is intentionally headless: useLayer returns triggerProps, layerProps, and renderLayer helpers that you spread onto your own elements, so you never fight the library over styling. Sensible defaults get you started quickly, while a rich options object (placement, possiblePlacements, auto, triggerOffset, containerOffset) exposes fine-grained control. Documentation on react-laag.com and a Storybook keep the learning curve reasonable despite the flexibility.