Form Handling in Headless WordPress: Three Approaches
Forms are everywhere – contact forms, newsletter signups, lead capture. Here’s how to handle them in a headless WordPress setup.
Approach 1: WordPress Forms (Recommended)
Use Contact Form 7 or WPForms in WordPress, submit from Next.js:
// Server action in Next.js
async function handleSubmit(formData: FormData) {
const response = await fetch(
'https://cms.flatwp.com/wp-json/contact-form-7/v1/contact-forms/123/feedback',
{
method: 'POST',
body: formData,
}
);
return response.json();
}
Pros:
- Editors manage form fields in WordPress
- Email notifications handled by WordPress
- Submissions stored in WordPress database
- Works with existing WordPress plugins
Cons:
- Extra API call to WordPress
- WordPress becomes a dependency for forms
Approach 2: Next.js Server Actions
Handle forms entirely in Next.js with server actions:
// app/contact/actions.ts
'use server'
export async function submitContact(formData: FormData) {
const data = {
name: formData.get('name'),
email: formData.get('email'),
message: formData.get('message'),
};
// Send email via Resend, SendGrid, etc.
await sendEmail(data);
// Store in database if needed
await db.contacts.create(data);
}
Pros:
- No WordPress dependency
- Full control over validation and processing
- Modern server actions pattern
- Can integrate with any email service
Cons:
- Editors can’t manage form fields
- Need separate storage for submissions
- More code to maintain
Approach 3: Third-Party Services
Use Formspree, Tally, or similar:
<form action="https://formspree.io/f/your-id" method="POST">
<input name="email" type="email" />
<button type="submit">Submit</button>
</form>
Pros:
- Zero backend code
- Spam protection included
- Email notifications handled
- Nice dashboard for submissions
Cons:
- Monthly cost ($0-$20)
- Less customization
- Another service to manage
FlatWP’s Approach
We include adapters for all three approaches. Our default recommendation:
- Use WordPress forms for marketing sites (editors need control)
- Use server actions for apps (more dynamic needs)
- Use third-party for MVPs (fastest to ship)
The beauty of headless is you’re not locked in. Start with one, switch to another as needs change.
Validation with Zod
Regardless of approach, validate with Zod:
const contactSchema = z.object({
name: z.string().min(2),
email: z.string().email(),
message: z.string().min(10),
});
export async function submitContact(formData: FormData) {
const data = contactSchema.parse({
name: formData.get('name'),
email: formData.get('email'),
message: formData.get('message'),
});
// data is now typed and validated
}
Type-safe forms with runtime validation. Beautiful.
FlatWP Team
Related Posts
WordPress Plugin Compatibility: What Works with FlatWP
A comprehensive guide to WordPress plugin compatibility with FlatWP. Learn which plugins work out of the box and which need custom integration.
Preview Mode: Let Editors See Drafts Before Publishing
Learn how FlatWP implements draft preview mode, letting WordPress editors see unpublished content on your Next.js site before going live.