@leung99/react-modal
v2.0.3
Published
A flexible and animated modal library for React with GSAP animations
Maintainers
Readme
@leung99/react-modal
A flexible and powerful modal library for React with beautiful GSAP animations. Support centered modals and bottom sheets with customizable styles.
✨ Features
- 🎨 Smooth Animations: Powered by GSAP for fluid, professional animations
- 🎭 Two Modal Types: Centered modal with scale animation and bottom sheet with slide-up effect
- 🎯 Simple API: Event-based modal manager for opening modals from anywhere
- 💅 Highly Customizable: Custom styles via className or inline styles
- ⚡ Lightweight: Minimal dependencies, optimized performance
- 🔧 TypeScript: Full type definitions included
- ♻️ React Hooks: Built with modern React patterns
📦 Installation
npm install @leung99/react-modal gsap
# or
yarn add @leung99/react-modal gsap
# or
pnpm add @leung99/react-modal gsapNote: GSAP is a peer dependency and must be installed separately.
🚀 Quick Start
1. Add ModalProvider to your app
Place the ModalProvider component at the root level of your app (typically in App.tsx or _app.tsx):
import { ModalProvider } from '@leung99/react-modal';
function App() {
return (
<>
{/* Your app content */}
<YourComponents />
{/* Add ModalProvider at the end */}
<ModalProvider />
</>
);
}2. Open a modal from anywhere
import { modal } from '@leung99/react-modal';
function MyComponent() {
const handleOpen = () => {
modal.open({
render: ({ close, done }) => (
<div>
<h2>Hello Modal!</h2>
<button onClick={close}>Close</button>
<button onClick={() => done({ result: 'success' })}>Submit</button>
</div>
),
onClose: () => console.log('Modal closed'),
onDone: (data) => console.log('Submitted:', data),
});
};
return <button onClick={handleOpen}>Open Modal</button>;
}3. Use with useModal hook
The useModal hook provides access to modal context when used inside modal content:
import { modal, useModal } from '@leung99/react-modal';
function ModalContent() {
const { close, done, id } = useModal();
return (
<div>
<h2>Modal Content</h2>
<p>Modal ID: {id}</p>
<button onClick={close}>Cancel</button>
<button onClick={() => done({ value: 'data' })}>Submit</button>
</div>
);
}
// Open it
modal.open({
id: 'content-modal',
render: <ModalContent />,
});📖 API Reference
modal.open(options)
Opens a new modal with the specified configuration.
Options
| Property | Type | Required | Default | Description |
|----------|------|----------|---------|-------------|
| id | string | ✅ Yes | - | Unique identifier for the modal |
| render | ReactNode \| Component | ✅ Yes | - | Modal content to render |
| type | 'modal' \| 'bottomSheet' | No | 'modal' | Type of modal animation |
| isButtonClose | boolean | No | true | Show/hide the close button (×) |
| styleOverlay | string \| CSSProperties | No | - | Custom styles for the overlay backdrop |
| styleBoxContent | string \| CSSProperties | No | - | Custom styles for the modal content box |
| styleButtonClose | string \| CSSProperties | No | - | Custom styles for the close button |
| onClose | () => void | No | - | Callback when modal is closed |
| onDone | (data) => void \| Promise<any> | No | - | Callback when done() is called |
Render Props
When using a function component for render, it receives these props:
interface ModalRenderProps<T = any> {
close: () => void; // Close the modal
done: (data: T) => void; // Submit data and close
}useModal()
Hook to access modal context. Must be used inside modal content.
Returns:
{
id: string; // Modal ID
close: () => void; // Close the modal
done: (data: any) => void; // Submit and close with data
type?: 'modal' | 'bottomSheet'; // Modal type
isButtonClose?: boolean; // Close button visibility
styleOverlay?: string | CSSProperties;
styleBoxContent?: string | CSSProperties;
styleButtonClose?: string | CSSProperties;
// ... other modal options
}<ModalProvider />
Provider component that manages and renders all modals. Must be included once in your app.
<ModalProvider />🎨 Examples
Basic Centered Modal
import { modal } from '@leung99/react-modal';
modal.open({
id: 'basic-modal',
render: ({ close }) => (
<div style={{ padding: '20px', minWidth: '300px' }}>
<h2>Basic Modal</h2>
<p>This is a centered modal with scale animation</p>
<button onClick={close}>Close</button>
</div>
),
});Bottom Sheet
modal.open({
id: 'bottom-sheet',
type: 'bottomSheet',
render: ({ close }) => (
<div style={{ padding: '20px' }}>
<h2>Bottom Sheet</h2>
<p>This slides up from the bottom of the screen</p>
<button onClick={close}>Close</button>
</div>
),
});Custom Styled Modal
You can use CSS classes or inline styles:
// Using CSS class
modal.open({
render: <YourComponent />,
styleOverlay: 'my-custom-overlay',
styleBoxContent: 'my-custom-content',
});
// Using inline styles
modal.open({
render: <YourComponent />,
styleBoxContent: {
background: 'linear-gradient(135deg, #667eea 0%, #764ba2 100%)',
borderRadius: '16px',
padding: '32px',
color: 'white',
},
styleButtonClose: {
color: 'white',
},
});Form Modal with Data Submission
import { useState } from 'react';
import { modal, useModal } from '@leung99/react-modal';
function FormModal() {
const [name, setName] = useState('');
const [email, setEmail] = useState('');
const { close, done } = useModal();
const handleSubmit = (e: React.FormEvent) => {
e.preventDefault();
done({ name, email });
};
return (
<form onSubmit={handleSubmit} style={{ minWidth: '300px' }}>
<h2>Contact Form</h2>
<input
type="text"
value={name}
onChange={(e) => setName(e.target.value)}
placeholder="Name"
required
/>
<input
type="email"
value={email}
onChange={(e) => setEmail(e.target.value)}
placeholder="Email"
required
/>
<div style={{ marginTop: '16px', display: 'flex', gap: '8px' }}>
<button type="button" onClick={close}>Cancel</button>
<button type="submit">Submit</button>
</div>
</form>
);
}
// Open and handle submission
modal.open({
id: 'form-modal',
render: <FormModal />,
onDone: async (data) => {
console.log('Form data:', data);
// You can perform async operations here
await sendToAPI(data);
},
});Modal Without Close Button
modal.open({
id: 'no-close-btn',
isButtonClose: false,
render: ({ close }) => (
<div>
<h2>No Close Button</h2>
<p>You must use the button below to close</p>
<button onClick={close}>Done</button>
</div>
),
});🎬 Animation Details
Centered Modal
- Entry: Scale from 0.8 to 1.0 with back easing + fade in + slight upward movement
- Exit: Scale down to 0.8 with fade out + downward movement
- Duration: 400ms entry, 300ms exit
Bottom Sheet
- Entry: Slide up from bottom (translateY: 100% → 0)
- Exit: Slide down to bottom (translateY: 0 → 100%)
- Duration: 400ms entry, 300ms exit
Overlay
- Entry: Fade in (opacity: 0 → 1) over 300ms
- Exit: Fade out (opacity: 1 → 0) over 200ms
All animations use GSAP's power2 easing for smooth, professional motion.
🔧 TypeScript
Full TypeScript support with comprehensive type definitions:
import type { Modal } from '@leung99/react-modal';
// Available types
type ModalOptions = Modal.ModalOptions;
type BaseModalProps = Modal.BaseModalProps;
type OptionsModalContext = Modal.OptionsModalContext;📝 License
MIT © Leung190299
🤝 Contributing
Contributions, issues, and feature requests are welcome!
Feel free to check the issues page.
📮 Support
If you like this project, please give it a ⭐️ on GitHub!
For questions or issues, please open an issue.
