Manage modals just like managing pages with a router in React.

Related tags

Overlay react modal
Overview

Nice Modal

NPM Build Status Coverage Status MIT licensed

This is a small, zero dependency utility to manage modals of your React application. It allows you to manage modals just like managing pages with a router in React. It's not a React modal component but should be used with other UI libraries which provide modal components like Material UI, Ant.Design, Bootstrap React, etc.

Motivation

Using modals in React is a bit frustrating. Think of that if you need to implement below UI:

Usually, the first question in your mind is where to declare the modal via JSX. As the dialog in the picture may be showed in any page, it doesn't belong to any page component. So you probally put it in the Root component, for example:

const Root = () => {
  const [visible, setVisible] = useState(false);
  // other logic ...
  return (
    <>
      <Main />
      <AdsModal visible={visible} />
    </>
  );
}

However, when you declare the modal in the root component, there are some issues:

  1. Not scalable. It's unreasonable to maintain the modal's state in the root component. When you need more modals you need to maintain much state, especially you need to maintain arguments for the modal.
  2. It's hard to show or hide the modal from children comopnonents. When you maintain the state in a component then you need to pass setVisible down to the place where you need to show or hide the modal. It makes things too complicated.

Unfortunately, most examples of using modals just follow this practice, it causes such confusions when managing modals in React.

I believe you must once encountered with the scenario that originally you only needed to show a modal when click a button, then when requirements changed, you need to open the same modal from a different place. Then you have to refactor your code to re-consider where to declare the modal. The root cause of such annoying things is just because we have not understood the essential of a modal.

To resolve the problems, we need to re-think how we implement modals in React.

Re-think the Modal Usage Pattern in React

According to the wikipedia, a modal can be described as:

A window that prevents the user from interacting with your application until he closes the window.

From the definition we can get a conclusion: a modal is a global view that's not necessarily related with a specific context.

This is very similar with the page concept in a single page UI application. The visibility/ state of modals should be managed globally because, from the UI perspective, a modal could be showed above any page/componnet. The only difference between modal and page is: a modal allows you to not leave the current page to do some separate tasks.

For pages management, we already have router framework like React Router, it helps to navigate to a page by URL. Actually, you can think URL a global id for a page. So, similarly, what if you assign a uniq id to a modal then show/hide it by the id? This is just how we designed NiceModal.

With NiceModal, you will be able to manage the modals in a gobal and unified way.

Features

Basically, nice-modal-react manages state of all modals at a global place (React context by default, optionally Redux). And it provides APIs to show/hide/remove a modal from the page. Here's the list of key features:

  • Be able to use with any UI library.
  • Zero dependency and small: ~2kb after gzip.
  • Modals are uncontrolled. That is, you can close itself in the modal component.
  • The code of your modal component is not executed if it's invisible.
  • It should not break the transitions of showing/hiding a modal.
  • Promise based. Besides using props to interact with the modal from the parent component, you can do it easier by promise.

Usage

Installation

# with yarn
yarn add --dev @ebay/nice-modal-react

# or with npm
npm install @ebay/nice-modal-react --save-dev

Create Your Modal Component

With NiceModal you can create a separate modal component easily. It's just the same as you create a normal component but wrap it with high order compponent by NiceModal.create. For example, below code shows how to create a dialog with Ant.Design:

modal.hide()} onCancel={() => modal.hide()} afterClose={() => modal.remove()} > Hello ${name}! ); }); ">
import { Modal } from 'antd';
import NiceModal, { useModal } from '@ebay/nice-modal-react';

export default NiceModal.create(({ name }) => {
  const modal = useModal();
  return (
    <Modal
      title="Hello Antd"
      onOk={() => modal.hide()}
      onCancel={() => modal.hide()}
      afterClose={() => modal.remove()}
    >
      Hello ${name}!
    </Modal>
  );
});

From the code, we can see:

  • The modal is uncontrolled. You can hide your modal inside the component regardless where it is showed.
  • The high order component created by NiceModa.create ensures your component is not executed before it becomes visible.
  • You can call modal.remove to remove your modal component from the React component tree to reserve transitions.

Next, let's see how to use the modal.

Using You Modal Component

There are very flexible APIs for you to manage modals. See below for the introduction.

Embed your application with NiceModal.Provider:

Since we will manage status of modals globally, the first thing is embedding your app with NiceModal provider, for example:

import NiceModal from '@ebay/nice-modal-react';
ReactDOM.render(
  <React.StrictMode>
    <NiceModal.Provider>
      <App />
    </NiceModal.Provider>
  </React.StrictMode>,
  document.getElementById('root'),
);

The provider will use React context to maintain all modals' status.

Use the modal by id

You can control a nice modal by id or the component itself.

Nice Modal Examples

); } ">
import NiceModal from '@ebay/nice-modal-react';
import MyAntdModal from './my-antd-modal'; // created by above code

// If use by id, need to register the modal component.
// Normally you create a modals.js file in your project
// and register all modals there.
NiceModal.register('my-antd-modal', MyAntdModal);
function App() {
  const showAntdModal = () => {
    // Show a modal with arguments passed to the component as props
    NiceModal.show('my-antd-modal', { name: 'Nate' })
  };
  return (
    <div className="app">
      <h1>Nice Modal Examples</h1>
      <div className="demo-buttons">
        <button onClick={showAntdModal}>Antd Modal</button>
      </div>
    </div>
  );
}

Use modal component without id

If you don't want to use a string to show/hide a modal, you can use the component directly.

import MyAntdModal from './MyAntdModal';
//...
NiceModal.show(MyAntdModal, { name: 'Nate' });
//...

Use modal with the hook

The useModal hook can not only be used inside a modal component but also any component by passing it a modal id/component:

import NiceModal, { useModal } from '@ebay/nice-modal-react';
import MyAntdModal from './my-antd-modal'; // created by above code

NiceModal.register('my-antd-modal', MyAntdModal);
//...
// if use with id, need to register it first
const modal = useModal('my-antd-modal');
// or if with component, no need to register
const modal = useModal(MyAntdModal);

//...
modal.show({ name: 'Nate' }); // show the modal
modal.hide(); // hide the modal
//...

Declare your modal instead of register

The nice modal component you created can be also used as a normal component by JSX, then you don't need to register it. For example:

Nice Modal Examples

); } ">
import NiceModal, { useModal } from '@ebay/nice-modal-react';
import MyAntdModal from './my-antd-modal'; // created by above code

function App() {
  const showAntdModal = () => {
    // Show a modal with arguments passed to the component as props
    NiceModal.show('my-antd-modal')
  };
  return (
    <div className="app">
      <h1>Nice Modal Examples</h1>
      <div className="demo-buttons">
        <button onClick={showAntdModal}>Antd Modal</button>
      </div>
      <MyAntdModal id="my-antd-modal" name="Nate" />
    </div>
  );
}

With this approach, you can get the benifits:

  • Inherit React context in the modal component under some component node.
  • Pass arguments to the modal componnent via props.

NOTE: if you show the component by id but the modal is not declared or registered. Nothing will happen but only a warning message in the dev console.

Using promise API

Besides using props to interact with the modal from the parent component, you can do it easier by promise. For example, we have a user list page with a add user button to show a dialog to add user. After user is added the list should refresh itself to refelect the change, then we can use below code:

NiceModal.show(AddUserModal)
  .then(() => {
    // When call modal.hide(payload) in the modal component
    // it will resolve the promise returned by `show` method.
    // fetchUsers will call the rest API and update the list
    fetchUsers()
  })
  .catch(err=> {
    // if modal.hide(new Error('something went wrong')), it will reject the promise
  }); 

You can see the live example on codesandbox.

Integrating with Redux

By default NiceModal uses React context and useReducer internally to manage modal state. However you can easily integrate it with Redux if you want to tracking/debugging the state change of your modals with Redux dev tools. Below code shows how to do it:

// First combine the reducer

// Passing Redux state to the nice modal provider

Using with any UI library

Using help methods

API Reference

License

MIT

Comments
  • [WIP] fix: react types for show() no longer make every prop optional

    [WIP] fix: react types for show() no longer make every prop optional

    Using Partial<Props> makes every component props optional. This was probably done in #9 because create() used to take Record<string, unknow> as a type parameter which did not provide type safety.

    By using P coming from React.ComponentType<P>, we are able to fix this.

    opened by geowarin-sg 13
  • Sequential use of the same Modal

    Sequential use of the same Modal

    Thanks for the package, looks great 💪

    I have a question, I'm trying to implement the same Modal to appear twice, one after the other. When the user submits the first Modal, the second will appear. Sadly, isn't working perfectly

    PS: I'm using bootstrap

    const inputModal = useModal(ModalInput)
    
    inputModal.show({ title: 'Step 1', defaultValue: 'Hello' }).then((r) => {
      inputModal.show({ title: 'Step 2', defaultValue: 'World' }).then((r) => alert(r.value))
    })
    

    That alert at the end, is reporting the value from the first Modal, also, the second defaultValue never appears, I guess the second Modal never shows. I tried added a hide() before the show() but couldn't make it work (maybe hide() doesn't return a promise so I can wait for it to hide)

    How should I make this work? Is this a limitation or can I have a workaround to make it work?

    enhancement 
    opened by plckr 13
  • No modal id found in NiceModal.useModal

    No modal id found in NiceModal.useModal

    Hi, I'm attempting to use this for the first time, in a Nextjs project, and am running into the error (server-side):

    Error: No modal id found in NiceModal.useModal

    When following the docs:

    import { Modal } from 'antd';
    import NiceModal, { useModal } from '@ebay/nice-modal-react';
    
    export default NiceModal.create(({ name }) => {
      // Use a hook to manage the modal state
      const modal = useModal();
      return (
        <Modal
          title="Hello Antd"
          onOk={() => modal.hide()}
          visible={modal.visible}
          onCancel={() => modal.hide()}
          afterClose={() => modal.remove()}
        >
          Hello {name}!
        </Modal>
      );
    });
    

    The main difference being that instead of using antd, I'm using a modal called Dialog from Radix UI

    I'll set up an example for you but in the meantime, am I doing something wrong that should be obvious?

    Thanks

    opened by dungle-scrubs 9
  • fix: rename antd visibility props to 'open' from 'visible'

    fix: rename antd visibility props to 'open' from 'visible'

    Fixed, antd deprecated warnings.

    Ant Design has deprecated the visible property. Using visible will cause a warning to be displayed in the console.

    Please see official explains.

    P.S. Thanks for the awesome module 😃

    opened by shoota 8
  • Add type safety to modal.show()

    Add type safety to modal.show()

    First of all thanks for a great library!

    I think it will be great if you can add type safety to the modal arguments when calling the show method. Could be using generics:

    modal.show<Props>(props: Props)

    Of course it will be amazing if somehow it will infer it from the modal component itself but I'm not sure it's possible and the generics solution at least gives basic type safety.

    Thanks!

    enhancement 
    opened by avivm 8
  • [Question] How to make TS warn me if I forget to pass Modal's props when calling NiceModal.show()

    [Question] How to make TS warn me if I forget to pass Modal's props when calling NiceModal.show()

    @supnate so I am basically using the Use modal with the hook pattern to show the modal as follows -

    import NiceModal, { useModal } from '@ebay/nice-modal-react';
    import ModalIWantToShow from './ModalIWantToShow.tsx'; // created by above code
    
    const modalIWantToShow = useModal(ModalIWantToShow);
    
    //...
    modalIWantToShow.show({ name: 'Nate' }); // show the modal
    //...
    

    Now the problem with the above is that I may forget to pass the props while calling 'show()'. Is there a way to make sure that TS warns me if I have forgot to pass the props [because the props are not optional most of the times]??

    Edit - @supnate There is another big problem with this. Let's suppose we have a modal, which initially have no props to it. Now later the requirements change and we have to add props to that modal. Now in this case someone might think, that where ever I need props TS will warn and I will add the props [especially if its a big project and the modal is being used at a lot of places]. After compiling I find there are no errors and thus I move forward. But actually whereever I used the modal with hook, I am in trouble because right now it's not warning if we don't pass props. So this is not just about 'try and remember to pass the props if using show()'. This behaviour defeats the purpose of TS altogether

    opened by nik32 6
  • Need to know modal status

    Need to know modal status

    Before all thank you for this awesome lib ! ❤️

    In our project we use this lib for manage all modals and it's just perfect.

    However, we have some error handlers to manage error when we call apis.

    In these handlers, we can open a modal (with Nicemodal.show we can) but we want to know which modals are open. For that, I would like to pass the state to the handler.

    Would it be possible to export the state or the context ?

    Thank you !

    opened by youf-olivier 6
  • How to avoid multiple triggers when modal.keepMounted is true

    How to avoid multiple triggers when modal.keepMounted is true

    Thanks a lot for such a great library that makes the code better split!

    I wanted to keep the user's temporary input and found that modal.keepMounted = true would do it

    but this will cause the callback function to be triggered multiple times on the last submit, and I want it to be called only once at the end.

    code Link

    bug 
    opened by ficapy 6
  • Help with sequential async modal using Radix Primitive's Dialog component

    Help with sequential async modal using Radix Primitive's Dialog component

    Hi, I'm really close to getting this library to work with Radix Primitives Dialog component, but am not sure what I'm doing wrong.

    I'm able to get a normal modal working by connecting useModal's methods to Dialog's props. However, when I attempt to get a sequential modal going while following your async example, it doesn't seem to work after the first modal.

    Here's the sandbox that I currently have working.

    Are you able to spot what I'm doing wrong?

    opened by dungle-scrubs 5
  • fix: useModal show handle no longer making every prop optional and add NiceModa.show component props generic

    fix: useModal show handle no longer making every prop optional and add NiceModa.show component props generic

    This PR addresses this other PR where it was discussed how to make the types more flexible, but apparently the pr was discontinued.

    So, based on @jakst example I created this PR.

    examples: missing_required_modal_props

    required_props_prepared

    I think the user should be able to pass a generic to say what props are expected. modal_show

    Here I think the user should pass the required props because, as far as I'm aware, when we add the modal component directly in NiceModal.show(), we don't have to register the modal or anything. show_modal_component

    opened by alexandredev3 5
  • Generic type error on `NiceModal.create`

    Generic type error on `NiceModal.create`

    Hi, thanks for this great tool! I'm having little trouble with NiceModal.create method when it always require my component props to have an extra [key: string]: unknown property signature.

    Here's my modal component with normal prop declaration but fail because of incompatibility with Record<string, unknown>.

    image

    I always have to add this extra [key: string]: unknown property to dismiss the ts error.

    image
    opened by SevenOutman 5
  • [Request] Add a modal.removeResolve(false) helper function

    [Request] Add a modal.removeResolve(false) helper function

    In all my modal components, I either return a response, or if the user closes the modal I do something like this:

    <Modal
        onClose={() => {
           modal.resolve(false);
           modal.remove();
        }}
    >
    

    However, this is not ideal since I'm passing a new onClose function instance on each render. It would be great if there was a utility function on the modal instance that did both of these steps in one. 🙌

    opened by MarksCode 1
  • Support optional component props generic in `NiceModalArgs`?

    Support optional component props generic in `NiceModalArgs`?

    Hello, thanks for this great tool, it's a joy to work with!

    When passing optional props to the registered modal component (and the component type passes a condition in type NiceModalArgs<T>), the component props are typed as Partial<Omit<React.ComponentProps<T>, 'id'>>.

    Using Partial<> means that otherwise required component props are incorrectly loosened up. The related comment specifically mentions using Partial<>, so I guess there was a good reason to do so. But to achieve type safety, now a wrapper function must be used that enforces the required component props:

    function registerModalWithProps<C extends React.FC<any>>(id: string, component: C, props: React.ComponentProps<C>) {
      register(id, component, props)
    }
    

    Could this situation be avoided if an optional component props generic were to be introduced?

    type ReactJSXOrConstructor<T> = T extends keyof JSX.IntrinsicElements | React.JSXElementConstructor<any> ? T : never
    
    type PartialComponentProps<T> = Partial<React.ComponentProps<ReactJSXOrConstructor<T>>>
    
    declare type NiceModalArgs<T, P = PartialComponentProps<T>> = T extends ReactJSXOrConstructor<T> ? Omit<P, 'id'> : Record<string, unknown>;
    

    I understand the optional generic would creep into the signatures of useModal(), register(), and possibly even show(), so an on-demand wrapper function might be simpler overall.

    opened by oroszi1mark 0
  • returned object from useModal hook have

    returned object from useModal hook have "unstable" identity

    Hi, so nice library!

    and this fix for https://github.com/eBay/nice-modal-react/issues/53

    just in time (I've just updated to 1.2.4) Thank you!

    But could we do the same for whole object returned from useModal?

    const userModal = useModal(UserInfoModal);
    

    to make userModal referential equality all the time? so if I test it here like this:

    const userModal = useModal(UserInfoModal);
    
    React.useEffect(() => {
        console.log('endless show...');
        userModal.show();
    }, [userModal]) // <- referential identity will change every time and we have endless loop here
    
    enhancement 
    opened by mrudowski 4
  • `this` has been rewritten to `undefined`

    `this` has been rewritten to `undefined`

    When using rollup, a warning is shown:

    image

    It leads to https://rollupjs.org/guide/en/#error-this-is-undefined

    Is there a way to prevent this error?

    No other packages in project produce errors or warnings.

    opened by vladshcherbin 2
Releases(1.2.8)
Owner
eBay
https://ebay.github.io/
eBay
A react component for modals and dialogs

React SkyLight React SkyLight is a simple react component for modals and dialogs. Powerful, lightweight and customizable design. React SkyLight DEMOS

Marcio Gasparotto 569 Sep 29, 2022
Make reusable React cards, pop-ups or modals as an async function

About Create reusable asynchronous functional cards. Allows to create the cards used on the pages once and allows them to be called as an async functi

Mustafa KURU 7 Nov 15, 2021
😎 🍒 React hook for Portals, which renders modals, dropdowns, tooltips etc. to or else.

REACT COOL PORTAL This is a React hook for Portals. It helps you render children into a DOM node that exists outside the DOM hierarchy of the parent c

Welly 745 Nov 14, 2022
🖼 React hook for Modals

useModal ?? React hook for Modals Simple, lightweight hook for Modals/Dialogs. This hook is also isomorphic, meaning it works with SSR (server side re

Alex Cory 80 Nov 11, 2022
React Popup Component - Modals,Tooltips and Menus —  All in one

Reactjs-popup Reactjs-popup is a simple react popup component that helps you create simple and complex Modals, tooltips, and Menus for your next React

Youssouf EL AZIZI 1.6k Nov 24, 2022
Creating modals in React Framer Motion

Creating modals in React Framer Motion Features Installation Set up Live Demo What is Framer Motion? Framer Motion is a relatively new open source, pr

Fireship 70 Dec 6, 2022
A simple package to create modal gallery with react-router

react-router-modal-gallery This is a simple package for the react-router to implement url based modals. It is mainly based on the Modal Gallery exampl

Onur Önder 16 Oct 16, 2022
React Component to show a modal dialog with react-bootstrap.

React Bootstrap Dialog The React component library for an alert or dialog based on react-bootstrap's <Modal />. Configurable and easy use instead of w

Hiroki Akiyama 57 Oct 6, 2022
React-simple-modal-cb - A simple modal to display some customizable text, built with react

react-simple-modal-cb Here is the first NPM Package I built and I made it for my

CB23 0 Feb 9, 2022
React-dialog - A custom react hook to use a dialogs easily

react-dialog react-dialog is a custom react hook to use a dialog easily. Documen

Levy Mateus Macedo 2 Mar 29, 2022
React Hook Dialog - React hook for master your dialog component

?? React Hook Dialog React hook for master your dialog component ✨ Features ??‍♂️ TYPE SAFE ?? Awesome DX ?? Super light ?? Example Material UI Ant De

Justin Sun 41 Nov 9, 2022
A collection of dialog animations with React.js

Reboron A collection of dialog animations with React.js. Demo - https://jerairrest.github.io/reboron/ About This is a fork of http://yuanyan.github.io

Bold Commerce 26 Jul 16, 2022
A fully accessible React modal built according WAI-ARIA Authoring Practices

react-aria-modal SEEKING CO-MAINTAINERS! Continued development of this project is going to require the work of one or more dedicated co-maintainers (o

David Clark 977 Dec 3, 2022
Accessible modal dialog component for React

react-modal Accessible modal dialog component for React.JS Table of Contents Installation API documentation Examples Demos Installation To install, yo

React Community 7.1k Dec 3, 2022
Official SweetAlert2 enhancer adding support for React elements as content

sweetalert2-react-content Official SweetAlert2 enhancer adding support for React elements as content. The following options can be React elements: tit

SweetAlert2 561 Nov 24, 2022
Simple Modal Manager for React Projects

react-simple-modal-provider Simple Modal Manager for React Projects Contents Demo Installation Examples API Async Animation ETC Demo ⭐️ Codesandbox In

Taek 28 Sep 6, 2022
Tiny modal component for React and Next.js apps

A Tiny React modal component for nextjs built with simplicity and accessbility in mind

Makuza Mugabo Verite 3 Oct 4, 2021
Universal Model for React

Universal Model for React Universal model is a model which can be used with any of following UI frameworks: Angular 2+ universal-model-angular React 1

Universal Model 5 Feb 14, 2022
Simple and flexible modal dialog component for React JS

react-st-modal React St Modal is a simple and flexible library for implementing modal dialogs. Features Simple and easy to use api Compatible with mob

Oleg 41 Nov 6, 2022