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

Creating a React Form for Submissions in WordPress

Learn to build interactive React forms in WordPress using controlled components. Master state management and submission events for your admin dashboard.

ReactWordPressFormsControlled ComponentsJavaScriptphpplugin-development

Previously in this course, we covered working with @wordpress/components to build the visual shell of our admin dashboard. Now that we have the layout, it's time to make it functional. In this lesson, we are building the "Add Entry" form, which is the heart of our plugin's data entry interface.

To build effective user interfaces, we must master the concept of Forms and Controlled components. In React, a controlled component is a form element whose value is driven entirely by state, making the data flow predictable and easy to debug.

Understanding Controlled Components

In vanilla HTML, form elements like <input> or <textarea> maintain their own internal state (the text currently typed into the box). In React, we shift that responsibility to the component's state.

When you use a controlled component, the input's value attribute is tied to a variable in your useState hook. Every time the user types, an onChange event fires, and we update that state variable. React then re-renders the component with the new value. This cycle—State → UI → Event → State—is the foundation of modern React development.

Building the Knowledge Base Entry Form

Let’s implement a form to capture a title and description for our Knowledge Base. We will use the useState hook to track the input values and a handler function to manage form submission.

JSX
import { useState } from CE9178">'@wordpress/element';
import { Button, TextControl, TextareaControl } from CE9178">'@wordpress/components';

const KnowledgeBaseForm = () => {
    // Initialize state for form fields
    const [title, setTitle] = useState(CE9178">'');
    const [content, setContent] = useState(CE9178">'');

    const handleSubmit = (event) => {
        // Prevent the browser from performing a standard page reload
        event.preventDefault();
        
        console.log(CE9178">'Submitting:', { title, content });
        
        // Reset the form after submission
        setTitle(CE9178">'');
        setContent(CE9178">'');
    };

    return (
        <form onSubmit={handleSubmit}>
            <TextControl
                label="Entry Title"
                value={title}
                onChange={(value) => setTitle(value)}
            />
            <TextareaControl
                label="Entry Content"
                value={content}
                onChange={(value) => setContent(value)}
            />
            <Button isPrimary type="submit">
                Add Entry
            </Button>
        </form>
    );
};

export default KnowledgeBaseForm;

Breaking Down the Implementation

  1. State Initialization: We use useState('') to initialize our fields as empty strings.
  2. Binding: The value prop of the TextControl is explicitly set to our state variable (title). If you remove the onChange handler, the input would become read-only because the state never updates to reflect the user's typing.
  3. Event Handling: The handleSubmit function includes event.preventDefault(). This is critical; without it, the browser will attempt a traditional GET/POST request, triggering a page refresh that destroys your React application's state.

Hands-on Exercise: Adding a Category Field

Now it's your turn to extend the form.

  1. Add a new TextControl to the form above specifically for a "Category" field.
  2. Add a new state variable called category using useState.
  3. Update the handleSubmit function to include the category in the console.log output.
  4. Verify that the input clears after you click the "Add Entry" button.

Common Pitfalls

When building Forms with Controlled components, developers often trip over these three issues:

  • Forgetting event.preventDefault(): As mentioned, this is the #1 cause of "why does my page reload when I submit?". Always include it as the first line in your submit handler.
  • Missing onChange handlers: If you define a value prop on an input but omit the onChange handler, the input will appear "stuck" and the user won't be able to type anything.
  • Over-complicating State: For simple forms, individual useState calls are fine. However, if your form grows to 10+ fields, consider using a single state object with useState({ title: '', content: '', category: '' }) and a single change handler that updates the object using the spread operator: setFormData({ ...formData, [name]: value }).

By mastering these patterns, you ensure that your plugin's data layer remains consistent with the UI. In the next lesson, we will connect this form to our REST API to persist data to the database.

Up next: Implementing CRUD in the Admin UI

Similar Posts