A lightweight, promise-based confirm dialog for React, designed to be as easy to use as react-toastify, while remaining fully customizable.
- Try not to use versions 1.4.0 , 1.4.1 and 1.4.2 .
npm install react-confirm-lite
# or
yarn add react-confirm-lite
# or
pnpm add react-confirm-litePlace <ConfirmContainer /> in your app (usually in root layout) and use it with confirm
import { ConfirmContainer, confirm } from 'react-confirm-lite';
function App() {
async function handleAction() {
const result = await confirm('Are you sure?');
if (result) {
console.log('User confirmed!');
} else {
console.log('User cancelled!');
}
}
return (
<div>
{/* Your app content */}
<ConfirmContainer />
</div>
);
}By default it shows the closest container to button to which you clicked to show the confirmation dialog but you are a programmer and you will try thing and sometimes you may use it in useEffect hook then in this case it will show the first rendered confirm container.
If you want to show a specific container by confirm api no matters where it is then you can pass the id like this
// In confirm Api
confirm({id:'1' , message:'Are you sure?'})
// And in confirm Container
<ConfirmContainer id='1'/>And make sure that not to pass same id to different <ConfirmContainer /> In this way It will show both of these containers
const result = await confirm('Message here');
// Returns true for OK, false for Cancel, null for ESC/outside clickChoose from various animations:
slide(default) - Smooth slide up/downfadeScale- Fade with scale effectbounce- Playful bounce animationflip- 3D flip effectzoom- Zoom in/outrotate- Rotate animationfadeUp/fadeDown- Directional fadedrop- 3D drop effectslideRight/slideLeft- Horizontal slidesslideVertical- Vertical slideslideDown- Slide downrotateRight- Rotate from rightzoomSmall/bounceSmall- Subtle animationsfadeBlur/fadeShrink- Creative effects
Pre-built color themes:
dark(default) - Dark themelight- Light themeblue- Blue themered- Perfect for destructive actionsgreen- Success actionspurple- Premium/feature actions
closeOnEscape(default: true) - Press ESC to closecloseOnClickOutside(default: true) - Click backdrop to close- Returns
nullwhen closed via ESC or backdrop click
Control every aspect:
- Custom OK/Cancel button text
- Separate animation durations for enter/exit
- Override colors per dialog
- Custom CSS classes for all elements
Multiple confirm requests automatically queue and show one at a time:
// These will show sequentially
await confirm('First?');
await confirm('Second?');
await confirm('Third?');No CSS imports needed. Styles are automatically injected.
const result = await confirm('Delete this item?');
if (result) {
// User clicked OK
deleteItem();
} else if (result === false) {
// User clicked Cancel
console.log('Cancelled');
} else if (result === null) {
// User pressed ESC or clicked outside
console.log('Closed via ESC/backdrop');
}const result = await confirm({
title: 'Delete Account',
message: 'This will permanently delete your account and all data.',
okText: 'Delete Forever',
cancelText: 'Cancel',
colorSchema: 'red'
});<ConfirmContainer
closeOnEscape={false}
closeOnClickOutside={false}
/>
// Now dialog can only be closed with Cancel/OK buttons| Prop | Type | Default | Description |
|---|---|---|---|
id |
string |
random |
To show a specific container with a specific confirm() app |
animation |
AnimationType |
'slide' |
Animation type (18 options) |
animationDuration |
number |
300 |
Base animation duration (ms) |
animationDurationIn |
number |
- | Enter animation duration |
animationDurationOut |
number |
- | Exit animation duration |
defaultColorSchema |
ColorSchema |
'dark' |
Default color scheme |
closeOnEscape |
boolean |
true |
Close with ESC key |
closeOnClickOutside |
boolean |
true |
Close on backdrop click |
classes |
ConfirmClasses |
{} |
Custom CSS classes |
// String input (simple)
await confirm('Simple message');
// Object input (full options)
await confirm({
title: 'Custom Title', // Optional
message: 'Required message', // Required
okText: 'Proceed', // Optional
cancelText: 'Go Back', // Optional
colorSchema: 'blue', // Optional
});Return values:
true- User clicked OKfalse- User clicked Cancelnull- User pressed ESC or clicked outside (if enabled)
<ConfirmContainer
classes={{
overlay: "my-overlay-class", // Background overlay
wrapper: "my-modal-class", // Modal container
title: "my-title-class", // Title text
message: "my-message-class", // Message text
button: "my-button-class", // Both buttons
cancel: "my-cancel-class", // Cancel button only
ok: "my-ok-class", // OK button only
}}
/>Want complete control over the UI? Use the render prop:
<ConfirmContainer animationDuration={500}>
{({
isVisible,
confirm,
handleCancel,
handleOk,
containerRef,
animationClass,
animationStyle
}) => (
<div className={`modal-overlay ${isVisible ? 'show' : 'hide'}`}>
{/* Your custom backdrop */}
<div className="backdrop" onClick={handleCancel} />
{/* Your custom modal */}
<div
ref={containerRef}
className={`custom-modal ${animationClass}`}
style={animationStyle}
>
<h2>{confirm.title}</h2>
<p>{confirm.message}</p>
<div className="buttons">
<button onClick={handleCancel}>
{confirm.cancelText || 'Cancel'}
</button>
<button onClick={handleOk}>
{confirm.okText || 'OK'}
</button>
</div>
</div>
</div>
)}
</ConfirmContainer>Available render props:
isVisible: Boolean indicating if dialog should be visibleconfirm: The current confirm options objecthandleCancel: Function to cancel the dialog (returns false)handleOk: Function to confirm the dialog (returns true)containerRef: React ref for the modal containercolorSchema: Current color schemeanimationClass: CSS class for current animationanimationStyle: Style object with animation durationlockScroll: Boolean, If will be true then scroll will be locked when dialog will show.
const handleDelete = async () => {
// Force user to make explicit choice
const result = await confirm({
title: 'Delete Item',
message: 'This action cannot be undone. Are you sure?',
okText: 'Delete',
cancelText: 'Keep',
colorSchema: 'red'
});
// Result can only be true or false (no null since ESC/backdrop disabled)
if (result) {
await deleteItem();
}
};
// In your component
<ConfirmContainer closeOnEscape={false} closeOnClickOutside={false} />// Allow closing by clicking backdrop but not ESC
<ConfirmContainer closeOnEscape={false} closeOnClickOutside={true} />
const handleSubmit = async () => {
const result = await confirm({
title: 'Submit Form',
message: 'Ready to submit this form?',
okText: 'Submit',
cancelText: 'Review',
colorSchema: 'green'
});
if (result) {
await submitForm();
} else if (result === null) {
// User clicked backdrop
console.log('Closed by clicking outside');
}
};// Global: ESC and backdrop disabled
<ConfirmContainer
closeOnEscape={false}
closeOnClickOutside={false}
/>
// Some dialogs can override via custom UI
const handleFlexibleDialog = async () => {
// Create custom UI that allows ESC/backdrop
const result = await confirm('Flexible dialog?');
// result can be true, false, or null
};<ConfirmContainer
defaultColorSchema="light" // Light theme by default
animation="zoom" // Zoom animation for all dialogs
animationDuration={400} // 400ms animations
closeOnEscape={true} // Allow ESC to close
closeOnClickOutside={true} // Allow backdrop click to close
animationDurationIn={350} // Enter: 350ms
animationDurationOut={250} // Exit: 250ms
lockScroll={false} // true by default
/>// Option 1: Fully closable (default)
<ConfirmContainer closeOnEscape={true} closeOnClickOutside={true} />
// Users can close via: OK, Cancel, ESC, or backdrop click
// Option 2: Force explicit choice
<ConfirmContainer closeOnEscape={false} closeOnClickOutside={false} />
// Users can only close via: OK or Cancel buttons
// Option 3: Backdrop only
<ConfirmContainer closeOnEscape={false} closeOnClickOutside={true} />
// Users can close via: OK, Cancel, or backdrop click
// Option 4: ESC only
<ConfirmContainer closeOnEscape={true} closeOnClickOutside={false} />
// Users can close via: OK, Cancel, or ESC keyIf user will close dialog box by clicking outside or by pressing escape then it will return null
slide- Smooth vertical slide (default)slideRight/slideLeft- Horizontal slidesslideVertical- Vertical slideslideDown- Slide down
fadeScale- Fade with scalingfadeUp/fadeDown- Directional fadesfadeBlur- Fade with blur effectfadeShrink- Fade with shrink effect
flip- Card flip effectdrop- 3D drop animationrotate/rotateRight- Rotation effects
bounce/bounceSmall- Bounce effectszoom/zoomSmall- Zoom in/out
- Make sure you are on version
>=1.4 - Make sure you didn't pass same id to different
<ConfirmContainer />
- Make sure
<ConfirmContainer />is mounted - Check it's not conditionally rendered
- Check if
closeOnEscape={true}(default) - Ensure no other event is preventing ESC
- Try different browsers
- Verify
closeOnClickOutside={true}(default) - Check if any parent element is preventing clicks
- Verify animation name is correct
- Check browser console for errors
- Ensure you have
@types/reactinstalled - Update to latest TypeScript version
- Use
classesprop to override styles - Check CSS specificity
You can also use it in TanStack with react easily
// app/layout.tsx
import { ConfirmContainer } from 'react-confirm-lite';
export default function RootLayout({
children,
}: {
children: React.ReactNode;
}) {
return (
<html lang="en">
<body>
{children}
<ConfirmContainer />
</body>
</html>
);
}// Server actions
'use server';
import { confirm } from 'react-confirm-lite';
export async function serverAction() {
const result = await confirm('Confirm from server?');
if (result) {
// Perform action
} else if (result === null) {
// User pressed ESC or clicked outside
console.log('Action cancelled');
}
}// Old way (always returns true/false)
if (window.confirm('Delete?')) {
deleteItem();
}
// New way (returns true/false/null)
const result = await confirm('Delete?');
if (result === true) {
await deleteItem();
} else if (result === false) {
console.log('User clicked Cancel');
} else if (result === null) {
console.log('User pressed ESC');
}- No CSS imports needed
- Automatic queue system
- Built-in animations
- Zero configuration
- Three return states (true/false/null)
Thanks for your interest in contributing. This project is intentionally lightweight, so the contribution workflow is kept simple and explicit. Please read this fully before starting.
react-confirm-lite/
ββ src/ # Library source code
ββ dist/ # Built output (generated)
ββ example/ # Local playground app (Vite + React)
ββ CONTRIBUTING.md
ββ README.md
ββ package.json
ββ tsup.config.ts
- src/ β where you make changes
- dist/ β auto-generated by tsup (do not edit manually)
- example/ β used to test changes locally
- Node.js >= 18
- npm >= 9
- Basic familiarity with React + TypeScript
git clone https://github.com/SaadNasir-git/react-confirm-lite.git
cd react-confirm-litenpm installThis project uses tsup watch mode for live rebuilding.
From the project root:
npm run build:watchThis will:
- Watch
src/for changes - Automatically rebuild
dist/ - Re-run post-build steps when files change
cd example
npm install
npm run devOpen the provided local URL in your browser.
- Modify files inside
src/ - tsup automatically rebuilds the library
- Refresh the browser running the example app
- Verify behavior visually and via console logs
You do not need to:
- run
npm pack - reinstall the package
- publish to npm
This setup mirrors real-world library development.
- Logic in
src/ - Types in
types.ts - Styles / animations
- README documentation
dist/files manually- Version number (maintainer handles releases)
- Build configuration unless discussed
- Use TypeScript types explicitly
- Avoid unnecessary abstractions
- Prefer clarity over cleverness
- Keep bundle size in mind
When opening an issue, include:
- What you expected
- What actually happened
- Steps to reproduce
- Browser and React version
Feature requests are welcome, but keep in mind:
- This library aims to stay minimal
- Features should not add heavy dependencies
- API simplicity is a priority
If you want to contribute or modify the library locally, use the built-in example app and watch mode.
git clone https://github.com/SaadNasir-git/react-confirm-lite.git
cd react-confirm-lite
npm installIn the project root:
npm run build:watchThis watches the src/ directory and automatically rebuilds dist/ on every change.
In a second terminal:
cd example
npm install
npm run devOpen the local URL shown by Vite. Any change you make in src/ will be reflected after a browser refresh.
- Do not edit files inside
dist/manually - You do not need to run
npm packor reinstall the package - Versioning and releases are handled by the maintainer
For more details, see CONTRIBUTING.md.
By contributing, you agree that your contributions will be licensed under the MIT License.
Thanks for contributing to react-confirm-lite π
MIT License - free for personal and commercial use.
Saad Nasir - Creator of react-confirm-lite
β Found this useful? Give it a star on GitHub! β