MCPcopy Index your code
hub / github.com/eBay/nice-modal-react

github.com/eBay/nice-modal-react @1.2.13

Chat with this repo
repository ↗ · DeepWiki ↗ · release 1.2.13 ↗ · + Follow
676 symbols 4,127 edges 46 files 3 documented · 0% 3 cross-repo links updated 8d ago1.2.13 · 2023-10-03★ 2,28927 open issues
What it actually does AI analysis from the code graph — generated when you open this
loading…
README

Nice Modal

This is a small, zero dependency utility to manage modals in a natural way for React. It uses context to persist state of modals globally so that you can show/hide a modal easily either by the modal component or id.

You can also see the introduction at eBay tech blog.

NPM Downloads Build Status Coverage Status Demo MIT licensed

For example, you can use below code to show a modal anywhere:

import NiceModal from '@ebay/nice-modal-react';
import MyModal from './MyModal';

//...
NiceModal.show(MyModal, { someProp: 'hello' }).then(() => {
  // do something if the task in the modal finished.
});
//...

Or you can register the modal with an id so that you don't need to import the modal component to use it:

import NiceModal from '@ebay/nice-modal-react';
import MyModal from './MyModal';

NiceModal.register('my-modal', MyModal);

// you can use the string id to show/hide the modal anywhere
NiceModal.show('my-modal', { someProp: 'hello' }).then(() => {
  // do something if the task in the modal finished.
});
//...

NOTE: @ebay/nice-modal-react is not a React modal component but should be used with other modal/dialog implementions by UI libraries like Material UI, Ant.Design, Bootstrap React, etc.

Examples

You can see a list of examples at: https://ebay.github.io/nice-modal-react

Key Features

  • Zero dependency and small: ~2kb after gzip.
  • Uncontrolled. You can close modal itself in the modal component.
  • Decoupled. You don't have to import a modal component to use it. Modals can be managed by id.
  • The code of your modal component is not executed if it's invisible.
  • It doesn't 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.
  • Easy to integrate with any UI library.

Motivation

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

The dialog is used to create a JIRA ticket. It could be shown from many places, from the header, to the context menu, to the list page. Traditionally, we had declared modal components with a JSX tag. But then the question became, “Where should we declare the tag?”

The most common option was to declare it wherever it was being used. But using modals in a declarative way is not only about a JSX tag, but also about maintaining the modal’s state like visibility, parameters in the container component. Declaring it everywehre means managing state everywhere. It's frustrating.

The other option put it in the Root component, for example:

const Root = () => {
  const [visible, setVisible] = useState(false);
  // other logic ...
  return (
    <>
      <Main />
      <NewTicketModal 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 components. 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.

Rethink 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/component. 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.

However, besides using id, NiceModal allows to use the modal component directly to manage it.

Usage

Installation

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

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

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:

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>
  );
});

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 NiceModal.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 Your 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' state.

Using the modal by component

You can control a nice modal by the component itself.

import NiceModal 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(MyAntdModal, { name: 'Nate' })
  };
  return (



      <h1>Nice Modal Examples</h1>



        <button onClick={showAntdModal}>Antd Modal</button>






  );
}

Use the modal by id

You can also control a nice modal by id:

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 (



      <h1>Nice Modal Examples</h1>



        <button onClick={showAntdModal}>Antd Modal</button>






  );
}

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:

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 (



      <h1>Nice Modal Examples</h1>



        <button onClick={showAntdModal}>Antd Modal</button>



      <MyAntdModal id="my-antd-modal" name="Nate" />



  );
}

With this approach, you can get the benifits: * Inherit React context in the modal component under some component node. * Pass arguments to the modal component 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 reflect the change, then we can use below code:

NiceModal.show(AddUserModal)
  .then(() => {
    // When call modal.resolve(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.reject(new Error('something went wrong')), it will reject the promise
  }); 

You can see the live example on codesandbox.

Integrating with Redux

Though not necessary, you can integrate Redux to manage state of nice modals. Then you can use Redux dev tools to track/debug state change of modals. Here is how to do it:

// First combine the reducer
import { createStore, applyMiddleware, compose, combineReducers } from 'redux';
import { Provider, useSelector, useDispatch } from 'react-redux';
import NiceModal from '@ebay/nice-modal-react';
import { Button } from 'antd';
import { MyAntdModal } from './MyAntdModal';
import logger from 'redux-logger';

const composeEnhancers = (typeof window !== 'undefined' && window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__) || compose;
const enhancer = composeEnhancers(applyMiddleware(logger));

const store = createStore(
  combineReducers({
    modals: NiceModal.reducer,
    // other reducers...
  }),
  enhancer,
);

// Passing Redux state to the nice modal provider
const ModalsProvider = ({ children }) => {
  const modals = useSelector((s) => s.modals);
  const dispatch = useDispatch();
  return (
    <NiceModal.Provider modals={modals} dispatch={dispatch}>
      {children}
    </NiceModal.Provider>
  );
};

export default function ReduxProvider({ children }) {
  return (
    <Provider store={store}>
      <ModalsProvider>{children}</ModalsProvider>
    </Provider>
  );
}

Using with any UI library

NiceModal provides lifecyle methods to manage the state of modals. You can use modal handler returned by useModal hook to bind any modal like component to the state. Below are typical state and methods you will use:

  • modal.visible: the visibility of a modal.
  • modal.hide: will hide the modal, that is, change modal.visible to false.
  • modal.remove: remove the modal component from the tree so that you modal's code is not executed when it's invisible. Usually you call this method after the modal's transition.
  • modal.keepMounted if you don't want to remove the modal from the tree for some instances, you can decide if call modal.remove based on value of keepMounted.

Based on these properties/methods, you can easily use NiceModal with any modal-like component provided by any UI libraries.

Using help methods

As you already saw, we use code similar with below to manage the modal state:

```jsx //... const modal = useModal(); return ( modal.hide()} onCancel={() => modal.hide()} afterClose={(

Extension points exported contracts — how you extend this code

ProcessEnv (Interface)
(no doc)
example/src/react-app-env.d.ts
NiceModalState (Interface)
(no doc)
src/index.tsx
NiceModalStore (Interface)
(no doc)
src/index.tsx
NiceModalAction (Interface)
(no doc)
src/index.tsx
NiceModalCallbacks (Interface)
(no doc)
src/index.tsx
NiceModalHandler (Interface)
(no doc)
src/index.tsx

Core symbols most depended-on inside this repo

n
called by 1274
docs/static/js/2.039ea410.chunk.js
o
called by 225
docs/static/js/2.039ea410.chunk.js
i
called by 217
docs/static/js/2.039ea410.chunk.js
t
called by 194
docs/static/js/2.039ea410.chunk.js
r
called by 185
docs/static/js/2.039ea410.chunk.js
a
called by 169
docs/static/js/2.039ea410.chunk.js
s
called by 135
docs/static/js/2.039ea410.chunk.js
u
called by 134
docs/static/js/2.039ea410.chunk.js

Shape

Function 663
Interface 7
Method 4
Class 2

Languages

TypeScript100%

Modules by API surface

docs/static/js/2.039ea410.chunk.js514 symbols
src/index.tsx35 symbols
docs/static/js/main.6de8500e.chunk.js23 symbols
docs/static/js/3.016409b4.chunk.js23 symbols
src/index.test.js16 symbols
example/public/prism.js7 symbols
docs/prism.js7 symbols
example/src/PromiseSample.jsx5 symbols
example/src/Test.tsx4 symbols
example/config/modules.js4 symbols
example/src/useHash.js3 symbols
example/config/getHttpsConfig.js3 symbols

Used by 3 indexed graphs manifest dependencies, hub-wide

For agents

$ claude mcp add nice-modal-react \
  -- python -m otcore.mcp_server <graph>

⬇ download graph artifact

Ask about this repo answers extend the page