{"id":13496278,"url":"https://github.com/eBay/nice-modal-react","last_synced_at":"2025-03-28T18:31:49.125Z","repository":{"id":37250964,"uuid":"399490811","full_name":"eBay/nice-modal-react","owner":"eBay","description":"A modal state manager for React.","archived":false,"fork":false,"pushed_at":"2024-10-14T07:33:00.000Z","size":8980,"stargazers_count":2068,"open_issues_count":25,"forks_count":119,"subscribers_count":11,"default_branch":"main","last_synced_at":"2024-10-29T15:06:07.759Z","etag":null,"topics":["modal","react"],"latest_commit_sha":null,"homepage":"https://ebay.github.io/nice-modal-react","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/eBay.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.md","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2021-08-24T14:14:06.000Z","updated_at":"2024-10-29T06:13:10.000Z","dependencies_parsed_at":"2024-02-18T07:33:11.717Z","dependency_job_id":"a10e8bb6-7eda-487f-8a6d-e35c92986bf7","html_url":"https://github.com/eBay/nice-modal-react","commit_stats":{"total_commits":196,"total_committers":28,"mean_commits":7.0,"dds":0.5255102040816326,"last_synced_commit":"9b589e6326be8eff7ef1826167bb806ba62cf812"},"previous_names":[],"tags_count":8,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/eBay%2Fnice-modal-react","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/eBay%2Fnice-modal-react/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/eBay%2Fnice-modal-react/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/eBay%2Fnice-modal-react/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/eBay","download_url":"https://codeload.github.com/eBay/nice-modal-react/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":243882711,"owners_count":20363180,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["modal","react"],"created_at":"2024-07-31T19:01:44.987Z","updated_at":"2025-03-28T18:31:46.951Z","avatar_url":"https://github.com/eBay.png","language":"TypeScript","readme":"# Nice Modal\n\nThis 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.\n\n\u003e You can also see the introduction at [eBay tech blog](https://medium.com/ebaytech/rethink-modals-management-in-react-cf3b6804223d).\n\u003e \n\u003e ***Also check out our another nice utility! [nice-form-react](https://github.com/eBay/nice-form-react)! 😜***\n\n[![NPM](https://img.shields.io/npm/v/@ebay/nice-modal-react.svg)](https://www.npmjs.com/package/@ebay/nice-modal-react)\n[![Downloads](https://img.shields.io/npm/dm/@ebay/nice-modal-react.svg)](https://www.npmjs.com/package/@ebay/nice-modal-react)\n[![Build Status](https://api.travis-ci.com/eBay/nice-modal-react.svg?branch=main)](https://app.travis-ci.com/github/eBay/nice-modal-react)\n[![Coverage Status](https://codecov.io/gh/ebay/nice-modal-react/branch/main/graph/badge.svg)](https://codecov.io/github/eBay/nice-modal-react)\n[![Demo](https://img.shields.io/badge/demo-link-orange.svg)](https://ebay.github.io/nice-modal-react/)\n[![MIT licensed](https://img.shields.io/badge/license-MIT-blue.svg)](https://github.com/eBay/nice-modal-react/blob/main/LICENSE.md)\n\n\n\nFor example, you can use below code to show a modal anywhere:\n\n```jsx\nimport NiceModal from '@ebay/nice-modal-react';\nimport MyModal from './MyModal';\n\n//...\nNiceModal.show(MyModal, { someProp: 'hello' }).then(() =\u003e {\n  // do something if the task in the modal finished.\n});\n//...\n```\n\nOr you can register the modal with an id so that you don't need to import the modal component to use it:\n```jsx\nimport NiceModal from '@ebay/nice-modal-react';\nimport MyModal from './MyModal';\n\nNiceModal.register('my-modal', MyModal);\n\n// you can use the string id to show/hide the modal anywhere\nNiceModal.show('my-modal', { someProp: 'hello' }).then(() =\u003e {\n  // do something if the task in the modal finished.\n});\n//...\n\n```\n\n**NOTE**: `@ebay/nice-modal-react` is not a React modal component but should be used with other modal/dialog implementations by UI libraries like [Material UI](https://material-ui.com/), [Ant.Design](https://ant.design), [Bootstrap React](https://react-bootstrap.github.io/), etc.\n\n# Examples\nYou can see a list of examples at: https://ebay.github.io/nice-modal-react\n\n# Key Features\n* Zero dependency and small: ~2kb after gzip.\n* Uncontrolled. You can close modal itself in the modal component.\n* Decoupled. You don't have to import a modal component to use it. Modals can be managed by id.\n* The code of your modal component is not executed if it's invisible.\n* It doesn't break the transitions of showing/hiding a modal.\n* Promise based. Besides using props to interact with the modal from the parent component, you can do it easier by promise.\n* Easy to integrate with any UI library.\n\n# Motivation\nUsing modals in React is a bit frustrating. Think of that if you need to implement the below UI:\n\n\u003cimg src=\"images/modal-example.jpg\" width=\"700px\"/\u003e\n\nThe 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?”\n\nThe 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, and parameters in the container component. Declaring it everywhere means managing the state everywhere. It's frustrating.\n\nThe other option put it in the Root component, for example:\n\n```jsx\nconst Root = () =\u003e {\n  const [visible, setVisible] = useState(false);\n  // other logic ...\n  return (\n    \u003c\u003e\n      \u003cMain /\u003e\n      \u003cNewTicketModal visible={visible} /\u003e\n    \u003c/\u003e\n  );\n}\n```\n\nHowever, when you declare the modal in the root component, there are some issues:\n\n1. 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.\n2. 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.\n\nUnfortunately, most examples of using modals just follow this practice, it causes such confusions when managing modals in React.\n\nI believe you must once encountered with the scenario that originally you only needed to show a modal when you 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 essentials of a modal.\n\n# Rethink the Modal Usage Pattern in React\nAccording to the [Wikipedia](https://en.wikipedia.org/wiki/Modal_window), a modal can be described as:\n\n\u003e A window that prevents the user from interacting with your application until he closes the window.\n\nFrom the definition, we can get a conclusion: a modal is a global view that's not necessarily related with a specific context.\n\nThis 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 shown above any page/component. The only difference between a modal and a page is: a modal allows you to not leave the current page to do some separate tasks.\n\nFor pages management, we already have router framework like React Router, it helps to navigate to a page by URL. Actually, you can think of a URL as a global id for a page. So, similarly, what if you assign a unique id to a modal and then show/hide it by the id? This is just how we designed NiceModal.\n\n# Usage\n## Installation\n\n```bash\n# with yarn\nyarn add @ebay/nice-modal-react\n\n# or with npm\nnpm install @ebay/nice-modal-react\n```\n\n## Create Your Modal Component\nWith NiceModal you can create a separate modal component easily. It's just the same as creating a normal component but wrapping it with high order component by `NiceModal.create`. For example, the below code shows how to create a dialog with [Ant.Design](https://ant.design):\n\n```jsx\nimport { Modal } from 'antd';\nimport NiceModal, { useModal } from '@ebay/nice-modal-react';\n\nexport default NiceModal.create(({ name }: { name: string }) =\u003e {\n  // Use a hook to manage the modal state\n  const modal = useModal();\n  return (\n    \u003cModal\n      title=\"Hello Antd\"\n      onOk={() =\u003e modal.hide()}\n      visible={modal.visible}\n      onCancel={() =\u003e modal.hide()}\n      afterClose={() =\u003e modal.remove()}\n    \u003e\n      Hello {name}!\n    \u003c/Modal\u003e\n  );\n});\n```\n\nFrom the code, we can see:\n* The modal is uncontrolled. You can hide your modal inside the component regardless of where it is shown.\n* The high order component created by `NiceModal.create` ensures your component is not executed before it becomes visible.\n* You can call `modal.remove` to remove your modal component from the React component tree to reserve transitions.\n\nNext, let's see how to use the modal.\n\n## Using Your Modal Component\nThere are very flexible APIs for you to manage modals. See below for the introduction.\n\n### Embed your application with `NiceModal.Provider`:\nSince we will manage the status of modals globally, the first thing is embedding your app with NiceModal provider, for example:\n\n```js\nimport NiceModal from '@ebay/nice-modal-react';\nReactDOM.render(\n  \u003cReact.StrictMode\u003e\n    \u003cNiceModal.Provider\u003e\n      \u003cApp /\u003e\n    \u003c/NiceModal.Provider\u003e\n  \u003c/React.StrictMode\u003e,\n  document.getElementById('root'),\n);\n```\n\nThe provider will use React context to maintain all modals' state.\n\n### Using the modal by component\nYou can control a nice modal by the component itself.\n```js\nimport NiceModal from '@ebay/nice-modal-react';\nimport MyAntdModal from './my-antd-modal'; // created by above code\n\nfunction App() {\n  const showAntdModal = () =\u003e {\n    // Show a modal with arguments passed to the component as props\n    NiceModal.show(MyAntdModal, { name: 'Nate' })\n  };\n  return (\n    \u003cdiv className=\"app\"\u003e\n      \u003ch1\u003eNice Modal Examples\u003c/h1\u003e\n      \u003cdiv className=\"demo-buttons\"\u003e\n        \u003cbutton onClick={showAntdModal}\u003eAntd Modal\u003c/button\u003e\n      \u003c/div\u003e\n    \u003c/div\u003e\n  );\n}\n```\n\n### Use the modal by id\nYou can also control a nice modal by id:\n```js\nimport NiceModal from '@ebay/nice-modal-react';\nimport MyAntdModal from './my-antd-modal'; // created by above code\n\n// If you use by id, you need to register the modal component.\n// Normally you create a modals.js file in your project\n// and register all modals there.\nNiceModal.register('my-antd-modal', MyAntdModal);\n\nfunction App() {\n  const showAntdModal = () =\u003e {\n    // Show a modal with arguments passed to the component as props\n    NiceModal.show('my-antd-modal', { name: 'Nate' })\n  };\n  return (\n    \u003cdiv className=\"app\"\u003e\n      \u003ch1\u003eNice Modal Examples\u003c/h1\u003e\n      \u003cdiv className=\"demo-buttons\"\u003e\n        \u003cbutton onClick={showAntdModal}\u003eAntd Modal\u003c/button\u003e\n      \u003c/div\u003e\n    \u003c/div\u003e\n  );\n}\n```\n\n\n### Use modal with the hook\nThe `useModal` hook can not only be used inside a modal component but also any component by passing it a modal id/component:\n\n```jsx\nimport NiceModal, { useModal } from '@ebay/nice-modal-react';\nimport MyAntdModal from './my-antd-modal'; // created by above code\n\nNiceModal.register('my-antd-modal', MyAntdModal);\n//...\n// if you use with id, you need to register it first\nconst modal = useModal('my-antd-modal');\n// or if with component, no need to register\nconst modal = useModal(MyAntdModal);\n\n//...\nmodal.show({ name: 'Nate' }); // show the modal\nmodal.hide(); // hide the modal\n//...\n```\n\n### Declare your modal instead of `register`\nThe 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:\n\n```jsx\nimport NiceModal, { useModal } from '@ebay/nice-modal-react';\nimport MyAntdModal from './my-antd-modal'; // created by above code\n\nfunction App() {\n  const showAntdModal = () =\u003e {\n    // Show a modal with arguments passed to the component as props\n    NiceModal.show('my-antd-modal')\n  };\n  return (\n    \u003cdiv className=\"app\"\u003e\n      \u003ch1\u003eNice Modal Examples\u003c/h1\u003e\n      \u003cdiv className=\"demo-buttons\"\u003e\n        \u003cbutton onClick={showAntdModal}\u003eAntd Modal\u003c/button\u003e\n      \u003c/div\u003e\n      \u003cMyAntdModal id=\"my-antd-modal\" name=\"Nate\" /\u003e\n    \u003c/div\u003e\n  );\n}\n```\n\nWith this approach, you can get the benefits:\n* Inherit React context in the modal component under some component node.\n* Pass arguments to the modal component via props.\n\n\u003e NOTE: If you attempt to show the component by ID but the modal is not declared or registered, nothing will happen except for a warning message in the dev console.\n\n### Using promise API\n\nBesides using props to interact with the modal from the parent component, you can do more easily by promise. For example, we have a user list page with an 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:\n\n```jsx\nNiceModal.show(AddUserModal)\n  .then(() =\u003e {\n    // When call modal.resolve(payload) in the modal component\n    // it will resolve the promise returned by `show` method.\n    // fetchUsers will call the rest API and update the list\n    fetchUsers()\n  })\n  .catch(err=\u003e {\n    // if modal.reject(new Error('something went wrong')), it will reject the promise\n  }); \n```\n\nYou can see the live example on codesandbox.\n\n### Integrating with Redux\nThough not necessary, you can integrate Redux to manage the state of nice modals. Then you can use Redux dev tools to track/debug state change of modals. Here is how to do it:\n\n```jsx\n// First combine the reducer\nimport { createStore, applyMiddleware, compose, combineReducers } from 'redux';\nimport { Provider, useSelector, useDispatch } from 'react-redux';\nimport NiceModal from '@ebay/nice-modal-react';\nimport { Button } from 'antd';\nimport { MyAntdModal } from './MyAntdModal';\nimport logger from 'redux-logger';\n\nconst composeEnhancers = (typeof window !== 'undefined' \u0026\u0026 window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__) || compose;\nconst enhancer = composeEnhancers(applyMiddleware(logger));\n\nconst store = createStore(\n  combineReducers({\n    modals: NiceModal.reducer,\n    // other reducers...\n  }),\n  enhancer,\n);\n\n// Passing Redux state to the nice modal provider\nconst ModalsProvider = ({ children }) =\u003e {\n  const modals = useSelector((s) =\u003e s.modals);\n  const dispatch = useDispatch();\n  return (\n    \u003cNiceModal.Provider modals={modals} dispatch={dispatch}\u003e\n      {children}\n    \u003c/NiceModal.Provider\u003e\n  );\n};\n\nexport default function ReduxProvider({ children }) {\n  return (\n    \u003cProvider store={store}\u003e\n      \u003cModalsProvider\u003e{children}\u003c/ModalsProvider\u003e\n    \u003c/Provider\u003e\n  );\n}\n```\n\n### Using with any UI library\nNiceModal provides lifecycle 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 states and methods you will use:\n\n* ***modal.visible**: the visibility of a modal.\n* ***modal.hide**: will hide the modal, that is, change `modal.visible` to false.\n* ***modal.remove**: remove the modal component from the tree so that your modal's code is not executed when it's invisible. Usually, you call this method after the modal's transition.\n* ***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 the value of `keepMounted`.\n\nBased on these properties/methods, you can easily use NiceModal with any modal-like component provided by any UI libraries.\n\n### Using help methods\nAs you already saw, we use code similar with below to manage the modal state:\n\n```jsx\n//...\nconst modal = useModal();\nreturn (\n  \u003cModal\n    visible={modal.visible}\n    title=\"Hello Antd\"\n    onOk={() =\u003e modal.hide()}\n    onCancel={() =\u003e modal.hide()}\n    afterClose={() =\u003e modal.remove()}\n  \u003e\n    Hello NiceModal!\n  \u003c/Modal\u003e\n);\n//...\n```\n\nIt binds `visible` property to the `modal` handler, and uses `modal.hide` to hide the modal when close button is clicked. And after the close transition it calls `modal.remove` to remove the modal from the dom node.\n\nFor every modal implementation, we always need to do these bindings manually. So, to make it easier to use we provided helper methods for 3 popular UI libraries Material UI, Ant.Design and Bootstrap React.\n\n\n```jsx\nimport NiceModal, {\n  muiDialog,\n  muiDialogV5,\n  antdModal,\n  antdModalV5,\n  antdDrawer,\n  antdDrawerV5,\n  bootstrapDialog\n} from '@ebay/nice-modal-react';\n\n//...\nconst modal = useModal();\n// For MUI\n\u003cDialog {...muiDialog(modal)}\u003e\n\n// For MUI V5\n\u003cDialog {...muiDialogV5(modal)}\u003e\n\n// For ant.design\n\u003cModal {...antdModal(modal)}\u003e\n\n// For ant.design v4.23.0 or later\n\u003cModal {...antdModalV5(modal)}\u003e\n\n// For antd drawer\n\u003cDrawer {...antdDrawer(modal)}\u003e\n\n// For antd drawer v4.23.0 or later\n\u003cDrawer {...antdDrawerV5(modal)}\u003e\n\n// For bootstrap dialog\n\u003cDialog {...bootstrapDialog(modal)}\u003e\n\n```\n\nThese helpers will bind modal's common actions to correct properties of the component. However, you can always override the property after the helper's property. For example:\n\n```jsx\nconst handleSubmit = () =\u003e {\n  doSubmit().then(() =\u003e {\n    modal.hide();\n  });\n}\n\u003cModal {...antdModal(modal)} onOk={handleSubmit}\u003e\n```\n\nIn the example, the `onOk` property will override the result from `antdModal` helper.\n\n## API Reference\nhttps://ebay.github.io/nice-modal-react/api/\n\n## Testing\n\nYou can test your nice modals with tools like `@testing-library/react`.\n\n```jsx\nimport NiceModal from '@ebay/nice-modal-react';\nimport { render, act, screen } from '@testing-library/react';\nimport { MyNiceModal } from '../MyNiceModal';\n\ntest('My nice modal works!', () =\u003e {\n  render(\u003cNiceModal.Provider /\u003e\n  \n  act(() =\u003e {\n    NiceModal.show(MyNiceModal);\n  });\n  \n  expect(screen.getByRole('dialog')).toBeVisible();\n});\n```\n\n## Contribution Guide\n```bash\n# 1. Clone repo\ngit clone https://github.com/eBay/nice-modal-react.git\n\n# 2. Install deps\ncd nice-modal-react\nyarn\n\n# 3. Make local repo as linked\nyarn link\n\n# 4. Start dev server\nyarn dev\n\n# 5. Install examples deps\ncd example\nyarn\n\n# 6. Use local linked lib\nyarn link @ebay/nice-modal-react\n\n# 7. Start examples dev server\nyarn start\n```\n\nThen you can access http://localhost:3000 to see the examples.\n\n## FAQ\n### Can I get context in the component tree in a modal?\nYes. To get the data from context in the component tree you need to use the declarative way. For example:\n```jsx\nexport default function AntdSample() {\n  return (\n    \u003c\u003e\n      \u003cButton type=\"primary\" onClick={() =\u003e NiceModal.show('my-antd-modal', { name: 'Nate' })}\u003e\n        Show Modal\n      \u003c/Button\u003e\n      \u003cMyAntdModal id=\"my-antd-modal\" {...otherProps} /\u003e\n    \u003c/\u003e\n  );\n}\n```\nSee more [here](https://github.com/eBay/nice-modal-react/issues/104).\n\n# License\nMIT\n\n\n\n","funding_links":[],"categories":["TypeScript","UI Components"],"sub_categories":["Dialog/Modal/Alert"],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FeBay%2Fnice-modal-react","html_url":"https://awesome.ecosyste.ms/projects/github.com%2FeBay%2Fnice-modal-react","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FeBay%2Fnice-modal-react/lists"}