Implementing CRUD in the Admin UI: WordPress REST API & React
Master CRUD operations in your WordPress admin dashboard. Learn to trigger API requests from React forms and lists to build truly interactive plugins.
Previously in this course, we covered Building the Knowledge Base Service Layer | WordPress REST API to centralize our fetch logic. In this lesson, we move from passive data fetching to active data manipulation, connecting our React components to that service layer to perform full CRUD operations.
The Lifecycle of an Admin Action
In a React-based WordPress admin, "CRUD" isn't just about sending a request; it's about the feedback loop. When a user submits a form or deletes an item, the application must:
- Initiate: Trigger the API request via our service layer.
- Wait: Display a loading state or visual feedback.
- Sync: Update the local component state to reflect the server's truth.
- Notify: Provide success or error feedback to the user.
If you skip step 3, your UI becomes "stale," leading to user confusion when items they just deleted still appear on the screen.
Triggering POST and DELETE Requests
We’ll use our KnowledgeBaseService to handle the heavy lifting. In your list component, you'll want to map over your data array and attach a handler to a Button component from Working with @wordpress/components for WordPress Admin UIs.
Worked Example: Deleting an Item
Here is how you handle a deletion operation, ensuring the UI refreshes once the server confirms the action.
JAVASCRIPTimport { useState } from CE9178">'react'; import { Button } from CE9178">'@wordpress/components'; import { deleteArticle } from CE9178">'./services/api'; // From our service layer const KnowledgeBaseList = ({ items, setItems }) => { const [isDeleting, setIsDeleting] = useState(false); const handleDelete = async (id) => { setIsDeleting(true); try { await deleteArticle(id); // Refresh UI by filtering out the deleted item setItems(items.filter(item => item.id !== id)); } catch (error) { console.error("Deletion failed:", error); alert("Failed to delete article."); } finally { setIsDeleting(false); } }; return ( <ul> {items.map(item => ( <li key={item.id}> {item.title} <Button isDestructive onClick={() => handleDelete(item.id)} disabled={isDeleting} > Delete </Button> </li> ))} </ul> ); };
Refreshing UI State
The most common mistake is failing to link the API response to the component state. In the example above, setItems(items.filter(...)) is the critical step. Because we are managing state locally, we must manually reconcile the UI with the database change.
For Create operations (POST), the flow is similar:
- Submit the form via
createArticle(data). - Receive the newly created object from the API response.
- Update the state:
setItems([...items, newArticle]).
Hands-on Exercise
- Open your
KnowledgeBaseListcomponent. - Implement a
handleDeletefunction that calls yourdeleteArticleservice method. - Ensure your
Buttoncomponent is disabled while the request is in flight to prevent multiple accidental clicks. - Update your
itemsstate using the functional update pattern to ensure you are working with the latest data.
Common Pitfalls
- Race Conditions: If a user clicks "Delete" multiple times before the first request finishes, you may trigger errors. Always disable your buttons during the
isDeletingorisSubmittingstate. - Ignoring Nonces: Never forget that these requests require a nonce for security. Ensure your
api-fetchsetup is correctly configured as discussed in Localizing Data for JavaScript in WordPress Plugins. - Silent Failures: Always wrap your service calls in
try/catchblocks. If the server returns a 403 (Permission Denied) or 500 (Internal Server Error), the user needs to know, and the application shouldn't just "hang."
Recap
We’ve successfully connected our React frontend to the backend API, allowing for real-time CRUD operations. By managing local state alongside our service layer calls, we ensure that our plugin remains performant and predictable. Remember: the API is the source of truth, but the React state is the source of the user's experience.
Up next: We will begin transitioning away from local component state to a more robust global architecture in Understanding WordPress Data Store Architecture.
Work with me

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.

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.