A powerful, type-safe modal management system for React applications. This library provides utilities to create, manage, and render modals programmatically with full TypeScript support.
A simpler and improved version of eBay's nice-modal-react - designed with modern React patterns and enhanced developer experience.
- Type-safe: Full TypeScript support with intelligent type inference
- Programmatic control: Open and close modals from anywhere in your app
- Performance optimized: Uses Zustand for efficient state management
- Component caching: Efficient component registration system
- Simple API: Intuitive methods for modal management
- Modern React: Built with modern React patterns and hooks
- Lightweight: Minimal dependencies (nanoid + zustand)
npm install @stainless-code/react-modal-manager
yarn add @stainless-code/react-modal-manager
pnpm add @stainless-code/react-modal-manager
bun add @stainless-code/react-modal-manager
First, wrap your app with the ModalProvider
:
import { ModalProvider } from "@stainless-code/react-modal-manager";
function App() {
return (
<ModalProvider>
{/* Your app components */}
<YourAppContent />
</ModalProvider>
);
}
import { createModal } from "@stainless-code/react-modal-manager";
// Create modal
export const myModal = createModal<{
title: string;
message: string;
}>(({ id, close, title, message }) => (
<div className="modal-overlay">
<div className="modal-content">
<h2>{title}</h2>
<p>{message}</p>
<button onClick={close}>Close</button>
</div>
</div>
));
// Usage in a component
function MyComponent() {
const handleOpenModal = () => {
// Open modal with props
const modalId = myModal.open({
title: "Hello World",
message: "This is a modal message!",
});
console.log("Modal opened with ID:", modalId);
};
return (
<div>
<button onClick={handleOpenModal}>Open Modal</button>
<button onClick={myModal.close}>Close Last Modal</button>
</div>
);
}
import { createModal } from "@stainless-code/react-modal-manager";
import { useState } from "react";
// Create a more complex modal
export const userModal = createModal<{
user: {
id: number;
name: string;
email: string;
};
onSave: (user: any) => void;
}>(({ id, close, user, onSave }) => {
const [editedUser, setEditedUser] = useState(user);
function handleSave() {
onSave(editedUser);
close(); // Close modal after saving
}
return (
<div className="modal-overlay">
<div className="modal-content">
<h2>Edit User</h2>
<input
value={editedUser.name}
onChange={(e) =>
setEditedUser({ ...editedUser, name: e.target.value })
}
/>
<button onClick={handleSave}>Save</button>
<button onClick={close}>Cancel</button>
</div>
</div>
);
});
// Usage - TypeScript will require the correct props
userModal.open({
user: {
id: 1,
name: "John",
email: "[email protected]",
},
onSave: (user) => console.log("User saved:", user),
});
// Modal that doesn't require any props
export const confirmModal = createModal(({ close }) => (
<div className="modal-overlay">
<div className="modal-content">
<h2>Are you sure?</h2>
<p>This action cannot be undone.</p>
<button onClick={close}>Cancel</button>
<button
onClick={() => {
console.log("Confirmed!");
close();
}}
>
Confirm
</button>
</div>
</div>
));
// Usage - no props needed
confirmModal.open();
The library provides full TypeScript support with intelligent type inference:
// Define modal with typed props
const myModal = createModal<{
name: string;
age: number;
}>(({ close, name, age }) => (
<div>
<p>
Hello {name}, you are {age} years old!
</p>
<button onClick={close}>Close</button>
</div>
));
// ✅ Correct usage - TypeScript enforces required props
myModal.open({ name: "John", age: 30 });
// ❌ TypeScript error - missing required props
myModal.open({ name: "John" }); // Error: Property 'age' is missing
// ❌ TypeScript error - wrong prop types
myModal.open({ name: "John", age: "30" }); // Error: Type 'string' is not assignable to type 'number'
// For modals without props, no type parameter needed
const simpleModal = createModal(({ close }) => (
<div>
<p>Simple modal content</p>
<button onClick={close}>Close</button>
</div>
));
simpleModal.open(); // ✅ Correct - no props needed
Provider component that manages modal state and rendering. Must wrap your application.
function ModalProvider({ children }: React.PropsWithChildren): JSX.Element;
Creates a modal instance with open/close methods.
function createModal<TProps = Record<string, unknown>>(
component: ModalComponent<TProps>,
): CreatedModal<TProps>;
Parameter | Type | Description |
---|---|---|
component |
ModalComponent<TProps> |
The modal component to be managed |
CreatedModal<TProps>
object with:
open(props?: TProps): string
- Opens the modal and returns its IDclose(): void
- Closes the most recently opened modal of this type
Props automatically injected into your modal components:
interface ModalProps {
id: string; // Unique modal identifier
close: () => void; // Function to close this specific modal
}
Type definition for modal components:
type ModalComponent<TProps = Record<string, unknown>> = React.ComponentType<
ModalProps & TProps
>;
Your modal components should accept ModalProps
combined with your custom props.
Direct access to the modal store (advanced usage):
const { modals, openModal, closeModal } = useModalStore();
Feel free to submit issues or pull requests to improve the library. Every bit of help is appreciated. 💖