Install @ebay/nice-modal-react
mainYou can install the package using either yarn or npm.
# with yarn
yarn add @ebay/nice-modal-react
# or with npm
npm install @ebay/nice-modal-reactrepository·main·Indexed 25 days ago
https://github.com/ebay/nice-modal-reactA zero-dependency utility for managing modals in React. It provides a global state management pattern allowing modals to be triggered by ID or component reference, decoupling them from the component tree. It includes a Promise API for handling modal outcomes, a useModal hook for lifecycle management, and helper functions for integration with UI libraries like Material UI, Ant Design, and Bootstrap.
You can install the package using either yarn or npm.
# with yarn
yarn add @ebay/nice-modal-react
# or with npm
npm install @ebay/nice-modal-reactYou can integrate nice-modal-react with Redux to track and debug modal state changes using Redux DevTools. To do this, add NiceModal.reducer to your root reducer and pass the modals state and dispatch function to the NiceModal.Provider.
import { createStore, applyMiddleware, combineReducers } from 'redux';
import { Provider, useSelector, useDispatch } from 'react-redux';
import NiceModal from '@ebay/nice-modal-react';
const store = createStore(
combineReducers({
modals: NiceModal.reducer,
// other reducers...
}),
);
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>
);
}// 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>
);
}Because nice-modal-react manages modal state globally, you must wrap your application with NiceModal.Provider. This provider uses React context to maintain the state of all modals.
import NiceModal from '@ebay/nice-modal-react';
import ReactDOM from 'react-dom';
import React from 'react';
import App from './App';
ReactDOM.render(
<React.StrictMode>
<NiceModal.Provider>
<App />
</NiceModal.Provider>
</React.StrictMode>,
document.getElementById('root'),
);The configuration manages disableHostCheck to prevent DNS rebinding attacks while allowing development in complex environments (like cloud environments or subdomains).
By default, disableHostCheck is set to true if no proxy is configured. If a proxy is used, host checking is enabled for security unless DANGEROUSLY_DISABLE_HOST_CHECK is explicitly set to 'true'.
You can also specify allowedHost via the public key to permit specific hosts.
Provider component. This enables the internal state management and the NiceModalPlaceholder which automatically renders registered modals when they are shown.You can show a modal by passing the component itself to NiceModal.show. You can also pass an object of props to the modal. NiceModal.show returns a Promise that resolves when the modal is hidden.
import NiceModal from '@ebay/nice-modal-react';
import MyModal from './MyModal';
//...
NiceModal.show(MyModal, { someProp: 'hello' }).then(() => {
// do something when the task in the modal finishes.
});
//...You can test modals by wrapping your test render in a NiceModal.Provider. Use act when calling NiceModal.show() to ensure state updates are processed correctly.
import NiceModal from '@ebay/nice-modal-react';
import { render, act, screen } from '@testing-library/react';
import { MyNiceModal } from '../MyNiceModal';
test('My nice modal works!', () => {
render(<NiceModal.Provider />);
act(() => {
NiceModal.show(MyNiceModal);
});
expect(screen.getByRole('dialog')).toBeVisible();
});import NiceModal from '@ebay/nice-modal-react';
import { render, act, screen } from '@testing-library/react';
import { MyNiceModal } from '../MyNiceModal';
test('My nice modal works!', () => {
render(<NiceModal.Provider />
act(() => {
NiceModal.show(MyNiceModal);
});
expect(screen.getByRole('dialog')).toBeVisible();
});To decouple the caller from the modal implementation, you can register a modal with a unique string ID using NiceModal.register. Once registered, you can trigger the modal anywhere in your application using that ID instead of importing the component.
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 when the task in the modal finishes.
});
//...To create a modal component, wrap your component with the NiceModal.create higher-order component. This ensures the component is only executed when it becomes visible. Inside the component, use the useModal hook to manage visibility and lifecycle.
Key behaviors:
modal.hide() from within the component itself.modal.remove() (e.g., in an afterClose callback) to remove the component from the React tree and preserve UI transitions.Note: nice-modal-react is a management utility, not a UI component. You should use it alongside a UI library like Ant Design, Material UI, or Bootstrap.
import { Modal } from 'antd';
import NiceModal, { useModal } from '@ebay/nice-modal-react';
export default NiceModal.create(({ name }: { name: string }) => {
// 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 useModal hook provides a controller object to manage a modal's lifecycle. You can pass either a registered ID or a component reference to the hook.
modal.show(props): Displays the modal.modal.hide(): Hides the modal.import NiceModal, { useModal } from '@ebay/nice-modal-react';
import MyAntdModal from './my-antd-modal';
NiceModal.register('my-antd-modal', MyAntdModal);
// Using with ID (requires registration)
const modalById = useModal('my-antd-modal');
// Using with Component (no registration required)
const modalByComp = useModal(MyAntdModal);
// Usage
modalById.show({ name: 'Nate' });
modalById.hide();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 you use with id, you 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
//...To control a modal by a string ID, you must first register the component using NiceModal.register(id, component). This is useful for centralizing modal definitions in a single file.
import NiceModal from '@ebay/nice-modal-react';
import MyAntdModal from './my-antd-modal';
// Register the modal component with an ID
NiceModal.register('my-antd-modal', MyAntdModal);
function App() {
const showAntdModal = () => {
// Show the registered modal by its ID
NiceModal.show('my-antd-modal', { name: 'Nate' })
};
return (
<div className="app">
<button onClick={showAntdModal}>Antd Modal</button>
</div>
);
}import NiceModal from '@ebay/nice-modal-react';
import MyAntdModal from './my-antd-modal'; // created by above code
// If you use by id, you 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 className="Nice Modal Examples">Nice Modal Examples</h1>
<div className="demo-buttons">
<button onClick={showAntdModal}>Antd Modal</button>
</div>
</div>
);
}To quickly bind nice-modal-react state to popular UI libraries, use the provided helper functions. These helpers map the modal controller's properties (like visible, hide, and remove) to the specific props required by the UI library.
Available Helpers:
muiDialog(modal), muiDialogV5(modal)antdModal(modal), antdModalV5(modal), antdDrawer(modal), antdDrawerV5(modal)bootstrapDialog(modal)Example Usage:
import NiceModal, { antdModal } from '@ebay/nice-modal-react';
const modal = useModal();
<Modal {...antdModal(modal)}>
Content
</Modal>Note: You can override properties after the helper is spread. For example, <Modal {...antdModal(modal)} onOk={customHandler} /> will use your customHandler instead of the default one provided by the helper.
import NiceModal, {
muiDialog,
muiDialogV5,
antdModal,
antdModalV5,
antdDrawer,
antdDrawerV5,
bootstrapDialog
} from '@ebay/nice-modal-react';
//...
const modal = useModal();
// For MUI
<Dialog {...muiDialog(modal)}>
// For MUI V5
<Dialog {...muiDialogV5(modal)}>
// For ant.design
<Modal {...antdModal(modal)}>
// For ant.design v4.23.0 or later
<Modal {...antdModalV5(modal)}>
// For antd drawer
<Drawer {...antdDrawer(modal)}>
// For antd drawer v4.23.0 or later
<Drawer {...antdDrawerV5(modal)}>
// For bootstrap dialog
<Dialog {...bootstrapDialog(modal)}>