npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@leung99/react-modal

v2.0.3

Published

A flexible and animated modal library for React with GSAP animations

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 gsap

Note: 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.