kitzo

React utilities library

useOverlay

Manage UI overlays (modals, drawers, popovers) and synchronize them with the browser navigation history. Requires a stable ID to ensure states are correctly restored across page unmounts and remounts.

⚠ When NOT to use this hook

Do not use useOverlay for simple modals or dialogs that you only intend to show once and never reopen via the browser's forward button. Because open() pushes a history entry, a user who closes the dialog with the browser's Back button can immediately reopen it by pressing Forward.

This hook is designed for history-aware overlays — ones where you intentionally want the overlay to be restorable through browser navigation (e.g. multi-step flows, deep-linked modals). For simple one-time dialogs, use plain useState instead.

Usage

The id parameter is required. It must be a stable string that uniquely identifies the overlay instance on the page.

OverlayExample.tsx
const { isOpen, open, close } = useOverlay('page-name:my-modal');

Interactive Demo

Open the modals below and try navigating using your browser's back button. Even if you navigate to another page and come back, the stable IDs ensure the overlay state is perfectly restored.

Try opening the modals and then use your browser's Back and Forward buttons.

Implementation Example

App.tsx
import { useOverlay } from 'kitzo';
import { Dialog, DialogContent } from './components/ui/dialog';

function MyComponent() {
  // Use page-scoped IDs for maximum reliability
  const modal = useOverlay('settings:profile-modal');

  return (
    <>
      <button onClick={modal.open}>Open Modal</button>

      <Dialog open={modal.isOpen} onOpenChange={(open) => !open && modal.close()}>
        <DialogContent>
          <h2>I am a history-aware modal!</h2>
          <button onClick={modal.close}>Close</button>
        </DialogContent>
      </Dialog>
    </>
  );
}

API

Parameters

ParameterTypeDescription
id
string

A stable unique identifier for the overlay. Required for history synchronization.

Return Values

Return ValueTypeDescription
isOpen
boolean

Whether the overlay is currently active and visible.

open
() => void

Opens the overlay by pushing a new entry to the browser history.

close
() => void

Closes the overlay by triggering a browser back navigation.

Best Practices

  • Stable IDs: Always provide a hardcoded string ID. Avoid using random IDs or IDs generated during render (like useId) if they might change when the component tree shifts.
  • Page Scoping: Prefix your IDs with the page or feature name (e.g., 'login:otp-modal') to avoid collisions with other overlays in the history stack.
  • LIFO Order: The hook handles stacking automatically using a history-based stack model. Back button will always close the most recently opened overlay.