Delete Confirmation Modal
Introduction: From Browser Alerts to a Designed Delete Modal
In the previous lesson, you built the Task Detail Page, where users could edit a task or delete it.
However, the delete action used a simple browser confirmation dialog (confirm()), which works but feels plain and inconsistent with your app’s design.
In this unit, you’ll replace that with a custom modal — a professionally styled confirmation box that fits your app’s visual identity and provides a better user experience.
You’ll learn how to:
- Build a reusable Modal component using React and Tailwind CSS.
- Handle open and close states, including basic Escape-key keyboard handling.
- Connect the modal to the delete action in the Task Detail Page.
By the end, deleting a task will feel smoother, safer, and better integrated into your UI.
Building the Modal Component
A modal is a dialog box that appears over your page content and demands user attention.
It’s often used for confirmations or important actions like deleting data.
While it’s open, the background content is visually dimmed, keeping attention on the modal.
This is a simplified learning modal: it handles Escape-to-close and clear button actions, but it does not implement a full production dialog accessibility baseline such as focus trapping, automatic initial focus placement, focus restoration after close, or making background content inert.
Here’s the complete code for your Modal component:
The Modal component is a self-contained, reusable unit. Let’s break it down.
Props Overview
The component receives the following props:
- isOpen — determines whether the modal is visible or hidden.
- onClose — a function to call when the user cancels or presses Escape.
- onConfirm — a function to call when the user confirms the action (deletion).
- title — text displayed at the top of the modal.
- children — any additional content or message passed between the modal’s opening and closing tags.
Visibility Logic
When the modal is closed (isOpen is false), it renders nothing at all — React returns null.
When isOpen is true, the modal markup is rendered in front of everything else on the page.
The Visual Layout
The main container:
fixed inset-0makes the modal cover the entire viewport.bg-black/50creates a semi-transparent dark background overlay behind the modal.
The inner div with max-w-md defines the centered white modal box.
Inside the modal box:
- The title
(<h2>)shows the dialog title (like “Delete Task”). - The children section displays any custom text passed in (“Are you sure you want to delete…”).
- Two buttons are aligned to the right: Cancel and Delete.
The useEffect Hook: Handling the Escape Key
Let’s unpack this step-by-step:
Purpose:
When the modal is open, users should be able to press the Escape key to close it — just like in native apps.
How it works:
- The function
handlerlistens for keydown events. - If the pressed key is
'Escape', it callsonClose()to close the modal.
Conditional Binding:
- The event listener is added only when the modal is open (
if (isOpen)). - When the modal closes, the cleanup function removes the listener immediately.
Why it’s inside useEffect:
- This ensures the listener is only attached to the document when needed.
- It prevents multiple handlers from stacking or persisting after the modal unmounts.
Dependencies:
[isOpen, onClose]means React will re-run this effect if either of these values change, ensuring that the listener is always up to date.
This design keeps the modal lightweight while introducing basic keyboard handling and React’s best practices for managing global event listeners.
Connecting the Modal to the Delete Action
Now that the Modal is ready, let’s connect it to your Task Detail Page so it opens when a user clicks the Delete button.
Here’s the updated page implementation:
Breaking Down the Logic
State Management
Controls whether the modal is visible.
When showDelete is true, the modal appears; otherwise, it stays hidden.
Opening the Modal
Clicking the Delete button sets showDelete(true) — opening the modal.
Modal Configuration
isOpenis tied to the state variable.onClosesets it back tofalse, closing the modal.onConfirmexecuteshandleDelete, performing the actual deletion.
The Delete Logic
Explanation:
- Sends a DELETE request to the backend via the
api.del()helper.
On success:
- Shows a confirmation toast.
- Calls
mutate('/api/tasks')to refresh the list cache. - Redirects the user back to
/tasksusingrouter.push().
This flow ensures that:
- The modal always asks for confirmation.
- Users can cancel safely or confirm confidently.
- Data and UI remain in sync after the action.
Summary
In this lesson, you:
- Replaced the native
confirm()dialog with a reusable, accessible Modal component. - Learned how to structure the modal with React props and conditional rendering.
- Used
useEffectto listen for the Escape key and handle cleanup properly. - Connected the modal to the delete action in the Task Detail Page.
- Maintained good UX by giving users visual feedback via toasts and redirecting after deletion.
You’ve now built a safer, cleaner, and more modern delete confirmation workflow.
In the next and final unit, you’ll enhance interactivity further by implementing optimistic updates and a “Mark Complete / Incomplete” toggle — so changes reflect instantly in the UI.
