@buiducnhat/better-modal
v0.0.6
Published
A small, headless React utility for declarative modal state management. Register modals once, open them from anywhere in your app, and await their results as Promises.
Downloads
617
Maintainers
Readme
better-modal
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
Features
- 🔌 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
Prerequisites
- Node.js 18+ or Bun 1.x
- React 19+
- Zustand 5+
Installation
# npm
npm install @buiducnhat/better-modal zustand
# bun
bun add @buiducnhat/better-modal zustandQuick Start
1. Add <ModalContainer /> to your app root
import { ModalContainer } from "@buiducnhat/better-modal";
function App() {
return (
<>
<YourRoutes />
<ModalContainer />
</>
);
}2. Create a modal
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>
),
);3. Open it from anywhere
// Inside or outside React components
const confirmed = await ConfirmModal.show({ title: "Delete this item?" });
if (confirmed) {
deleteItem();
}API Reference
createModal(id, Component)
Registers a modal component and returns a typed controller.
const Modal = createModal("my-modal", MyComponent);
Modal.show(props); // → Promise<result>
Modal.hide(); // close without resolvinguseModal(id)
Hook 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 entirely<ModalContainer />
Place once at your app root. Renders all active modals.
registerModal(id, Component)
Low-level alternative to createModal — registers a component without returning the controller.
Documentation
| 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 |
Development
# Install dependencies
bun install
# Type-check all packages
bun run check-types
# Lint and format
bun run check
# Build for publishing
bun run buildLicense
MIT
