Building a Modal Confirmation System for WordPress Plugins
Learn to build a safe, user-centric Modal confirmation system in your WordPress React admin UI to prevent accidental data loss during API operations.
Previously in this course, we explored handling conflict resolution to maintain data integrity during concurrent edits. In this lesson, we shift our focus to user-side safety: we will implement a robust Modal system to ensure users explicitly confirm destructive actions before we trigger any API requests.
The Problem: Accidental Data Loss
In a professional plugin, "Delete" buttons are dangerous. If a user accidentally clicks a button that triggers an immediate DELETE request, they lose data instantly. To provide a high-quality UX (User Experience), we must introduce a Confirmation step that interrupts the flow, forces the user to pause, and requires a deliberate action to proceed.
Managing Modal State
A modal should be a controlled component in React. Rather than embedding the modal logic directly inside your list items, you should lift the state to your parent component or a dedicated UI manager.
You need two pieces of state:
isOpen: A boolean to track visibility.targetId: The ID of the item currently marked for deletion (so we know what to delete once confirmed).
Building the Modal Component
We will leverage @wordpress/components to keep our UI consistent with the WordPress admin design system. Specifically, the Modal component handles the overlay and the focus trapping for us.
JSXimport { Modal, Button } from CE9178">'@wordpress/components'; import { useState } from CE9178">'@wordpress/element'; const DeleteConfirmationModal = ({ isOpen, onClose, onConfirm, itemName }) => { if (!isOpen) return null; return ( <Modal title="Confirm Deletion" onRequestClose={onClose} isDismissible={true} > <p>Are you sure you want to delete "{itemName}"? This action cannot be undone.</p> <div style={{ display: CE9178">'flex', justifyContent: CE9178">'flex-end', gap: CE9178">'10px' }}> <Button variant="tertiary" onClick={onClose}>Cancel</Button> <Button variant="primary" isDestructive onClick={onConfirm}>Delete Permanently</Button> </div> </Modal> ); };
Integrating with the API Flow
To integrate this into our Knowledge Base plugin, we need to connect the UI button click to the modal, and the modal confirmation to our existing service layer.
If you have already mastered implementing CRUD in the admin UI, you know that the delete function is likely an asynchronous call. Here is how we bridge the two:
JSXconst KnowledgeBaseList = () => { const [isModalOpen, setIsModalOpen] = useState(false); const [itemToDelete, setItemToDelete] = useState(null); const handleDeleteClick = (item) => { setItemToDelete(item); setIsModalOpen(true); }; const confirmDeletion = async () => { await deleteKnowledgeBaseItem(itemToDelete.id); // From your service layer setIsModalOpen(false); setItemToDelete(null); // Trigger UI refresh here }; return ( <> {/* List items with buttons */} <Button onClick={() => handleDeleteClick(item)}>Delete</Button> <DeleteConfirmationModal isOpen={isModalOpen} onClose={() => setIsModalOpen(false)} onConfirm={confirmDeletion} itemName={itemToDelete?.title} /> </> ); };
Hands-on Exercise
- Add a
DeleteConfirmationModalcomponent to your project. - Modify your existing list view to set the
itemToDeletestate when the delete button is clicked. - Ensure the
onConfirmcallback resets the modal state and invokes your API service. - Verify that the browser console shows no network requests until the "Delete Permanently" button is clicked.
Common Pitfalls
- Focus Management: Always ensure the
Modalis closed properly when the user pressesEscor clicks the backdrop. WordPress components handle this, but if you build a custom overlay, you must manageonKeyDownlisteners manually. - Z-Index Conflicts: If your modal appears behind other elements, check the
z-indexof your admin container. WordPress uses specific layers for its own UI; try to render your modal at the root level using a React Portal if necessary. - Stale State: When confirming, ensure you aren't using a stale ID from a previous interaction. Always clear the
itemToDeletestate after the API promise resolves.
Recap
We have moved beyond basic CRUD by adding a safety layer. By implementing a state-driven Modal for Confirmation, we prevent accidental data loss and provide a professional interface for your users. This pattern is essential for any plugin handling content modification.
Up next: We will implement comprehensive activity logging to track these deletions and other changes in our custom database tables.
Work with me

Custom WordPress Plugin Development
Custom WordPress & WooCommerce plugins built to standard โ by the developer behind a plugin with 5,000+ active installs and a SaaS with 10,000+ users.

Headless WordPress + Next.js Frontend Development
Keep WordPress for content, get a lightning-fast Next.js frontend. The best of both worlds โ familiar editing, modern speed.