A lightweight React library for declarative modal state management — open modals imperatively from anywhere in your app and await their results as Promises.
Stack: TypeScript · React 19 · Zustand 5 · Bun · Turborepo · Biome
- 🔌 Drop-in — place
<ModalContainer />once in your app root, done - ⚡ Imperative API — call
Modal.show(props)from anywhere, including outside React - 🤝 Promise-based results —
await Modal.show()and get the user's response - 🎯 Selective re-renders — each modal subscribes only to its own Zustand slice
- 🧩 Headless & UI-agnostic — works with any component library (MUI, shadcn, Radix, etc.)
- 🔒 Type-safe — full TypeScript support for modal props
- Node.js 18+ or Bun 1.x
- React 19+
- Zustand 5+
# npm
npm install @buiducnhat/better-modal zustand
# bun
bun add @buiducnhat/better-modal zustandimport { ModalContainer } from "@buiducnhat/better-modal";
function App() {
return (
<>
<YourRoutes />
<ModalContainer />
</>
);
}import { createModal } from "@buiducnhat/better-modal";
const ConfirmModal = createModal(
"confirm",
({ modal, title }: { modal: any; title: string }) => (
<dialog open={modal.visible}>
<p>{title}</p>
<button onClick={() => modal.resolve(true)}>Confirm</button>
<button onClick={() => modal.resolve(false)}>Cancel</button>
</dialog>
),
);// Inside or outside React components
const confirmed = await ConfirmModal.show({ title: "Delete this item?" });
if (confirmed) {
deleteItem();
}Registers a modal component and returns a typed controller.
const Modal = createModal("my-modal", MyComponent);
Modal.show(props); // → Promise<result>
Modal.hide(); // close without resolvingHook to access modal state inside a modal component.
const modal = useModal("my-modal");
modal.visible; // boolean — is the modal open?
modal.props; // props passed to show()
modal.resolve(val); // close + resolve the Promise
modal.reject(err); // close + reject the Promise
modal.hide(); // close without resolving
modal.remove(); // remove from store entirelyPlace once at your app root. Renders all active modals.
Low-level alternative to createModal — registers a component without returning the controller.
| Doc | Description |
|---|---|
| Project PDR | Product requirements and roadmap |
| Architecture | System design and data flow |
| Codebase Map | File structure and public API reference |
| Code Standards | Conventions, naming, and tooling |
# Install dependencies
bun install
# Type-check all packages
bun run check-types
# Lint and format
bun run check
# Build for publishing
bun run buildMIT