react-modal-back-button
v1.0.10
Published
A lightweight React hook that enables native back button support for modals and dialogs. Press the back button to close modals instead of navigating away.
Maintainers
Readme
react-modal-back-button
A lightweight React hook that enables native back button support for modals and dialogs. Press the back button to close modals instead of navigating away from the page.
Features
- 🎯 Simple API - Built-in state management, no need for extra useState
- 📱 Mobile-First - Perfect UX for mobile users
- ⚡ Lightweight - Zero dependencies, minimal footprint
- 🔧 Flexible - Works with any modal library or custom implementation
- 🎨 TypeScript - Full type safety included
- ⚙️ SSR Safe - Works with Next.js and other SSR frameworks
Installation
npm install react-modal-back-buttonyarn add react-modal-back-buttonpnpm add react-modal-back-buttonThe Problem
When a modal is open and users press the back button (especially on mobile), the browser navigates to the previous page instead of closing the modal. This breaks the expected user experience and can cause data loss.
The Solution
This hook intercepts back button presses and closes your modal instead, creating an intuitive mobile-native experience that users expect.
Usage
Basic Example
import { useModalBackButton } from 'react-modal-back-button';
function MyComponent() {
const { isOpen, open, close } = useModalBackButton();
return (
<div>
<button onClick={open}>
Open Modal
</button>
{isOpen && (
<div className="modal">
<h2>My Modal</h2>
<button onClick={close}>
Close
</button>
</div>
)}
</div>
);
}With Popular Modal Libraries
React Modal
import Modal from 'react-modal';
import { useModalBackButton } from 'react-modal-back-button';
function MyComponent() {
const { isOpen, open, close } = useModalBackButton();
return (
<>
<button onClick={open}>Open Modal</button>
<Modal isOpen={isOpen} onRequestClose={close}>
<h2>Hello Modal</h2>
<button onClick={close}>Close</button>
</Modal>
</>
);
}Material-UI (MUI)
import { Dialog, DialogTitle, DialogContent, Button } from '@mui/material';
import { useModalBackButton } from 'react-modal-back-button';
function MyComponent() {
const { isOpen, open, close } = useModalBackButton();
return (
<>
<Button onClick={open}>Open Dialog</Button>
<Dialog open={isOpen} onClose={close}>
<DialogTitle>Hello Dialog</DialogTitle>
<DialogContent>
Content here
</DialogContent>
</Dialog>
</>
);
}Headless UI
import { Dialog } from '@headlessui/react';
import { useModalBackButton } from 'react-modal-back-button';
function MyComponent() {
const { isOpen, open, close } = useModalBackButton();
return (
<>
<button onClick={open}>Open</button>
<Dialog open={isOpen} onClose={close}>
<Dialog.Panel>
<Dialog.Title>Title</Dialog.Title>
</Dialog.Panel>
</Dialog>
</>
);
}Options
const { isOpen, open, close, toggle } = useModalBackButton({
key: 'my-modal',
enabled: true,
pushStateOnOpen: true,
cleanupOnClose: true
});Option Details
key: A unique identifier for the modal. Useful when you have multiple modals. Auto-generated if not provided.enabled: Set tofalseto temporarily disable the hook without unmounting.pushStateOnOpen: Whentrue(default), adds a history entry when the modal opens. Set tofalseif you manage history yourself.cleanupOnClose: Whentrue(default), removes the history entry when you close the modal programmatically (e.g., clicking a close button). Set tofalseto keep the history entry.
How It Works
The hook manages browser history to create seamless back button support:
- Modal Opens: Pushes a new history entry to the browser stack
- Back Button Pressed: Intercepts the
popstateevent and closes the modal - Modal Closed Programmatically: Automatically removes the history entry to prevent navigation issues
- Smart Detection: Distinguishes between back button closes and programmatic closes to handle cleanup correctly
This ensures the back button works exactly as users expect, without breaking browser navigation.
API Reference
useModalBackButton(options?)
Parameters
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| options | UseModalBackButtonOptions | No | Configuration options |
Returns
| Property | Type | Description |
|----------|------|-------------|
| isOpen | boolean | Current state of the modal |
| open | () => void | Function to open the modal |
| close | () => void | Function to close the modal |
| toggle | () => void | Function to toggle modal state |
Options
| Option | Type | Default | Description |
|--------|------|---------|-------------|
| key | string | auto-generated | Unique identifier for the modal. Useful when managing multiple modals |
| enabled | boolean | true | Enable or disable the hook without unmounting |
| pushStateOnOpen | boolean | true | Whether to push a history entry when modal opens |
| cleanupOnClose | boolean | true | Whether to remove history entry when modal closes programmatically |
TypeScript
Full TypeScript support is included with complete type definitions.
import {
useModalBackButton,
UseModalBackButtonOptions,
UseModalBackButtonReturn
} from 'react-modal-back-button';
const options: UseModalBackButtonOptions = {
key: 'my-modal',
enabled: true,
pushStateOnOpen: true,
cleanupOnClose: true
};
const { isOpen, open, close, toggle }: UseModalBackButtonReturn = useModalBackButton(options);Advanced Usage
Multiple Modals
When working with multiple modals, provide unique keys to avoid conflicts:
function App() {
const settings = useModalBackButton({ key: 'settings-modal' });
const profile = useModalBackButton({ key: 'profile-modal' });
return (
<>
<button onClick={settings.open}>Open Settings</button>
<button onClick={profile.open}>Open Profile</button>
{settings.isOpen && <SettingsModal onClose={settings.close} />}
{profile.isOpen && <ProfileModal onClose={profile.close} />}
</>
);
}Toggle Function
Use the built-in toggle function for convenience:
function App() {
const { isOpen, toggle } = useModalBackButton();
return (
<>
<button onClick={toggle}>
{isOpen ? 'Close' : 'Open'} Modal
</button>
{isOpen && <Modal onClose={toggle} />}
</>
);
}Conditional Enabling
Disable the hook based on conditions without unmounting:
const isMobile = useMediaQuery('(max-width: 768px)');
const { isOpen, open, close } = useModalBackButton({
enabled: isMobile
});Custom History Management
If you manage history yourself, disable automatic pushing:
const { isOpen, open, close } = useModalBackButton({
pushStateOnOpen: false
});Browser Support
Works in all modern browsers that support the History API:
- Chrome/Edge (latest)
- Firefox (latest)
- Safari (latest)
- iOS Safari (latest)
- Chrome Android (latest)
FAQ
Q: Does this work with Next.js?
A: Yes! The hook is SSR-safe and works perfectly with Next.js and other SSR frameworks.
Q: Can I use this with multiple modals?
A: Yes, provide a unique key option for each modal instance.
Q: Does this affect my router?
A: No, the hook only manages history when the modal is open and cleans up properly.
Q: What happens if I press back multiple times?
A: Only the modal close is handled. Further back presses will navigate normally through your app's history.
Contributing
Contributions are welcome! Please feel free to submit a Pull Request.
License
MIT
