Headless Form Submission With the WordPress REST API
If you’re building a WordPress site, you need a good reason not to choose a WordPress form plugin. They are convenient and offer plenty of customizations that would take a ton of effort to build from scratch. They render the HTML, validate the data, store the submissions, and provide integration with third-party services.
But suppose we plan to use WordPress as a headless CMS. In this case, we will be mainly interacting with the REST API (or GraphQL). The front-end part becomes our responsibility entirely, and we can’t rely anymore on form plugins to do the heavy lifting in that area. Now we’re in the driver’s seat when it comes to the front end.
Forms were a solved problem, but now we have to decide what to do about them. We have a couple of options:
- Do we use our own custom API if we have such a thing? If not, and we don’t want to create one, we can go with a service. There are many good static form providers, and new ones are popping up constantly.
- Can we keep using the WordPress plugin we already use and leverage its validation, storage, and integration?
The most popular free form plugin, Contact Form 7, has a submission REST API endpoint, and so does the well-known paid plugin, Gravity Forms, among others.
From a technical standpoint, there’s no real difference between submitting the form‘s data to an endpoint provided by a service or a WordPress plugin. So, we have to decide based on different criteria. Price is an obvious one; after that is the availability of the WordPress installation and its REST API. Submitting to an endpoint presupposes that it is always available publicly. That’s already clear when it comes to services because we pay for them to be available. Some setups might limit WordPress access to only editing and build processes. Another thing to consider is where you want to store the data, particularly in a way that adheres to GPDR regulations.
When it comes to features beyond the submission, WordPress form plugins are hard to match. They have their ecosystem, add-ons capable of generating reports, PDFs, readily available integration with newsletters, and payment services. Few services offer this much in a single package.
Even if we use WordPress in the “traditional” way with the front end based on a WordPress theme, using a form plugin’s REST API might make sense in many cases. For example, if we are developing a theme using a utility-first CSS framework, styling the rendered form with fixed markup structured with a BEM-like class convention leaves a sour taste in any developer’s mouth.
The purpose of this article is to present the two WordPress form plugins submission endpoints and show a way to recreate the typical form-related behaviors we got used to getting out of the box. When submitting a form, in general, we have to deal with two main problems. One is the submission of the data itself, and the other is providing meaningful feedback to the user.
So, let’s start there.
The endpoints
Submitting data is the more straightforward part. Both endpoints expect a POST request, and the dynamic part of the URL is the form ID.
Contact Form 7 REST API is available immediately when the plugin is activated, and it looks like this:
1 |
|
If we’re working with Gravity Forms, the endpoint takes this shape:
1 |
|
The Gravity Forms REST API is disabled by default. To enable it, we have to go to the plugin’s settings, then to the REST API page, and check the “Enable access to the API” option. There is no need to create an API key, as the form submission endpoint does not require it.
Update (Sep. 10, 2024): The IDs are hashed in Contact Form 7 version 5.8, but can still to be found in the URL of the form’s editing page.
The body of the request
Our example form has five fields with the following rules:
- a required text field
- a required email field
- a required date field that accepts dates before October 4, 1957
- an optional textarea
- a required checkbox
For Contact Form 7’s request’s body keys, we have to define them with the form-tags syntax:
1 2 3 4 5 6 7 |
|
Gravity Forms expects the keys in a different format. We have to use an auto-generated, incremental field ID with the input_ prefix. The ID is visible when you are editing the field.
1 2 3 4 5 6 7 |
|
Submitting the data
We can save ourselves a lot of work if we use the expected keys for the inputs’ name attributes. Otherwise, we have to map the input names to the keys.
Putting everything together, we get an HTML structure like this for Contact Form 7:
1 |
|
In the case of Gravity Forms, we only need to switch the action and the name attributes:
1 |
|
Since all the required information is available in the HTML, we are ready to send the request. One way to do this is to use the FormData in combination with the fetch:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
|
We can send the submission with little effort, but the user experience is subpar, to say the least. We owe to users as much guidance as possible to submit the form successfully. At the very least, that means we need to:
- show a global error or success message,
- add inline field validation error messages and possible directions, and
- draw attention to parts that require attention with special classes.
Field validation
On top of using built-in HTML form validation, we can use JavaScript for additional client-side validation and/or take advantage of server-side validation.
When it comes to server-side validation, both Contact Form 7 and Gravity Forms offer that out of the box and return the validation error messages as part of the response. This is convenient as we can control the validation rules from the WordPress admin.
For more complex validation rules, like conditional field validation, it might make sense to rely only on the server-side because keeping the front-end JavaScript validation in sync with the plugins setting can become a maintenance issue.
If we solely go with the server-side validation, the task becomes about parsing the response, extracting the relevant data, and DOM manipulation like inserting elements and toggle class-names.
Response messages
The response when there is a validation error for Contact Form 7 look like this:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
|
On successful submission, the response looks like this:
1 2 3 4 5 6 |
|
Compared to this, Gravity Forms’ validation error response is more compact:
1 2 3 4 5 6 7 8 9 10 11 |
|
But the response on a successful submission is bigger:
1 2 3 4 5 6 7 |
|
While both contain the information we need, they don‘t follow a common convention, and both have their quirks. For example, the confirmation message in Gravity Forms contains HTML, and the validation message keys don’t have the input_ prefix — the prefix that’s required when we send the request. On the other side, validation errors in Contact Form 7 contain information that is relevant only to their front-end implementation. The field keys are not immediately usable; they have to be extracted.
In a situation like this, instead of working with the response we get, it’s better to come up with a desired, ideal format. Once we have that, we can find ways to transform the original response to what we see fit. If we combine the best of the two scenarios and remove the irrelevant parts for our use case, then we end up with something like this:
1 2 3 4 5 6 7 8 9 10 |
|
And on successful submission, we would set isSuccess to true and return an empty validation error object:
1 2 3 4 5 |
|
Now it’s a matter of transforming what we got to what we need. The code to normalize the Contact Forms 7 response is this:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
|
The code to normalize the Gravity Forms response winds up being this:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
|
We are still missing a way to display the validation errors, success messages, and toggling classes. However, we have a neat way of accessing the data we need, and we removed all of the inconsistencies in the responses with a light abstraction. When put together, it’s ready to be dropped into an existing codebase, or we can continue building on top of it.
There are many ways to tackle the remaining part. What makes sense will depend on the project. For situations where we mainly have to react to state changes, a declarative and reactive library can help a lot. Alpine.js was covered here on CSS-Tricks, and it’s a perfect fit for both demonstrations and using it in production sites. Almost without any modification, we can reuse the code from the previous example. We only need to add the proper directives and in the right places.
Wrapping up
Matching the front-end experience that WordPress form plugins provide can be done with relative ease for straightforward, no-fuss forms — and in a way that is reusable from project to project. We can even accomplish it in a way that allows us to switch the plugin without affecting the front end.
Sure, it takes time and effort to make a multi-page form, previews of the uploaded images, or other advanced features that we’d normally get baked right into a plugin, but the more unique the requirements we have to meet, the more it makes sense to use the submission endpoint as we don’t have to work against the given front-end implementation that tries to solve many problems, but never the particular one we want.
Using WordPress as a headless CMS to access the REST API of a form plugin to hit the submissions endpoints will surely become a more widely used practice. It’s something worth exploring and to keep in mind. In the future, I would not be surprised to see WordPress form plugins designed primarily to work in a headless context like this. I can imagine a plugin where front-end rendering is an add-on feature that’s not an integral part of its core. What consequences that would have, and if it could have commercial success, remains to be explored but is a fascinating space to watch evolve.
The above is the detailed content of Headless Form Submission With the WordPress REST API. For more information, please follow other related articles on the PHP Chinese website!

Hot AI Tools

Undresser.AI Undress
AI-powered app for creating realistic nude photos

AI Clothes Remover
Online AI tool for removing clothes from photos.

Undress AI Tool
Undress images for free

Clothoff.io
AI clothes remover

Video Face Swap
Swap faces in any video effortlessly with our completely free AI face swap tool!

Hot Article

Hot Tools

Notepad++7.3.1
Easy-to-use and free code editor

SublimeText3 Chinese version
Chinese version, very easy to use

Zend Studio 13.0.1
Powerful PHP integrated development environment

Dreamweaver CS6
Visual web development tools

SublimeText3 Mac version
God-level code editing software (SublimeText3)

Hot Topics











I see Google Fonts rolled out a new design (Tweet). Compared to the last big redesign, this feels much more iterative. I can barely tell the difference

Have you ever needed a countdown timer on a project? For something like that, it might be natural to reach for a plugin, but it’s actually a lot more

Everything you ever wanted to know about data attributes in HTML, CSS, and JavaScript.

At the start of a new project, Sass compilation happens in the blink of an eye. This feels great, especially when it’s paired with Browsersync, which reloads

Tartan is a patterned cloth that’s typically associated with Scotland, particularly their fashionable kilts. On tartanify.com, we gathered over 5,000 tartan

The inline-template directive allows us to build rich Vue components as a progressive enhancement over existing WordPress markup.

PHP templating often gets a bad rap for facilitating subpar code — but that doesn't have to be the case. Let’s look at how PHP projects can enforce a basic

One thing that caught my eye on the list of features for Lea Verou's conic-gradient() polyfill was the last item:
