Back to Blog
Lesson 39 of the Intermediate WordPress Plugins: REST API & React Admin course
WordPressJune 26, 20263 min read

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.

WordPressReactUXUIREST APIComponentsphpplugin-development

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:

  1. isOpen: A boolean to track visibility.
  2. 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.

JSX
import { 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:

JSX
const 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

  1. Add a DeleteConfirmationModal component to your project.
  2. Modify your existing list view to set the itemToDelete state when the delete button is clicked.
  3. Ensure the onConfirm callback resets the modal state and invokes your API service.
  4. Verify that the browser console shows no network requests until the "Delete Permanently" button is clicked.

Common Pitfalls

  • Focus Management: Always ensure the Modal is closed properly when the user presses Esc or clicks the backdrop. WordPress components handle this, but if you build a custom overlay, you must manage onKeyDown listeners manually.
  • Z-Index Conflicts: If your modal appears behind other elements, check the z-index of 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 itemToDelete state 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.

Similar Posts