Back to Blog

Form Handling in Headless WordPress: Three Approaches

FlatWP TeamFlatWP Team
2 min read

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

FlatWP Team

Related Posts