Nextjs stable v5 Netlify Forms stop sending data

Netlify forms submission have stopped working. It used to work for me before the stable v5 release of Nextjs.

The forms are correctly registered and submission seems submit but no data is captured.

'use client'
import { zodResolver } from '@hookform/resolvers/zod';
import axios from 'axios';
import React, { useRef, useState } from 'react';
import ReCAPTCHA from 'react-google-recaptcha';
import { useForm, SubmitHandler } from 'react-hook-form';
import toast, { Toaster } from 'react-hot-toast';
import { z } from 'zod';

interface IContactFormInput {
  name: string;
  email: string;
  enquiryType: string;
  message: string;
  agreeToTerms: boolean;
}

const schema = z.object({
  'form-name': z.string().optional(),
  name: z.string().min(1, 'Please enter your name'),
  email: z.string().email('Please enter a valid email address'),
  enquiryType: z.string().min(1, 'Please select the nature of your enquiry'),
  message: z.string().min(1, 'Please enter your message'),
  agreeToTerms: z.boolean().refine(val => val, 'You must agree to the Terms & Conditions')
});

export function ContactForm() {
  const captchaRef: any = useRef(null)
  const [isSubmitting, setIsSubmitting] = useState<boolean>(false);
  const [submissionSuccess, setSubmissionSuccess] = useState<boolean>(false);

  const key: string = process.env.NEXT_PUBLIC_RECAP_SITE_KEY || '';

  const { register, handleSubmit, formState: { errors }, reset } = useForm<IContactFormInput>({
    resolver: zodResolver(schema),
  });


  const onSubmit: SubmitHandler<IContactFormInput> = (data: any) => {
    setIsSubmitting(true);
    try {
      const formData:any = new FormData();

      for (const key in data) {
        formData.append(key, data[key]);
      }

      formData.append('form-name', 'contact');
      console.log('formData', new URLSearchParams(formData).toString());

      fetch("/", {
        method: "POST",
        headers: { "Content-Type": "application/x-www-form-urlencoded" },
        body: new URLSearchParams(formData).toString(),
      }).then((response) => {
        if (response) {
          setIsSubmitting(false);
          setSubmissionSuccess(true);
          toast.success('Your message has been sent!');
          reset(); // Optionally reset form fields
        }
      });
      

    } catch (error) {
      console.log('error', error);
      setIsSubmitting(false);
      toast.error('There was an error sending your message. Please try again later.');
      return;
    }


  };

  if (submissionSuccess) {
    return (<div className={'container mx-auto max-w-2xl'}>
        <div className="text-center font-bold">Your message has been sent successfully! We will get back to you soon.</div>
      </div>
    );
  }

  return (
    <div className="form-control w-full max-w-xl mx-auto" >
      <form onSubmit={handleSubmit(onSubmit)} noValidate data-netlify="true" method="post" className="space-y-4">
        <input type="hidden" name="form-name" value="contact" />
        <input
          type="text"
          placeholder="Name"
          className={`input input-bordered w-full ${errors.name ? 'input-error' : ''}`}
          {...register('name')}
          disabled={isSubmitting}
        />
        {errors.name && <p className="text-error">{errors.name.message}</p>}

        <input
          type="email"
          placeholder="Email"
          className={`input input-bordered w-full ${errors.email ? 'input-error' : ''}`}
          {...register('email')}
          disabled={isSubmitting}
        />
        {errors.email && <p className="text-error">{errors.email.message}</p>}

        <select
          className={`select select-bordered w-full ${errors.enquiryType ? 'select-error' : ''}`}
          {...register('enquiryType')}
          disabled={isSubmitting}
        >
          <option value="" disabled defaultValue="">Nature of Enquiry</option>
          <option value="general">General Inquiry</option>
          <option value="billing">Billing Inquiry</option>
          <option value="support">Support Request</option>
          <option value="feedback">Feedback</option>
          <option value="other">Other</option>
        </select>
        {errors.enquiryType && <p className="text-error">{errors.enquiryType.message}</p>}

        <textarea
          placeholder="Message"
          className={`textarea textarea-bordered w-full ${errors.message ? 'textarea-error' : ''}`}
          {...register('message')}
          rows={4}
          disabled={isSubmitting}
        />
        {errors.message && <p className="text-error">{errors.message.message}</p>}


        <div className="flex items-center gap-2">
          <div className="grow w-full">
            <div className="flex items-center  gap-2">
              <input
                type="checkbox"
                className={`checkbox ${errors.agreeToTerms ? 'checkbox-error' : ''}`}
                {...register('agreeToTerms')}
                id="agreeToTerms"
              />
              <label htmlFor="agreeToTerms" className="label-text">I agree to the Terms & Conditions</label>
            </div>
            {errors.agreeToTerms && <p className="text-error">{errors.agreeToTerms.message}</p>}
          </div>



          <div className="shrink flex w-full justify-end">
            {/* Submit Button */}
            <button type="submit" className={`btn btn-primary ${isSubmitting ? 'loading' : ''}`}
                    disabled={isSubmitting}>
              {isSubmitting ? 'Sending...' : 'Send Message'}
            </button>
          </div>
        </div>
      </form>
      <Toaster />
    </div>
  );
}

export default ContactForm;

I also get an error on my local server when submitting:

Error: Failed to find Server Action "null". This request might be from an older or newer deployment. Original error: Invariant: Missing 'next-action' header.
    at rw (/Users/olafsiebert/html/myelementalbeing/node_modules/next/dist/compiled/next-server/app-page.runtime.dev.js:39:1703)
    at /Users/olafsiebert/html/myelementalbeing/node_modules/next/dist/compiled/next-server/app-page.runtime.dev.js:38:8843
    at AsyncLocalStorage.run (node:async_hooks:346:14)
    at rS (/Users/olafsiebert/html/myelementalbeing/node_modules/next/dist/compiled/next-server/app-page.runtime.dev.js:38:8051)
    at r2 (/Users/olafsiebert/html/myelementalbeing/node_modules/next/dist/compiled/next-server/app-page.runtime.dev.js:41:1257)
    at /Users/olafsiebert/html/myelementalbeing/node_modules/next/dist/compiled/next-server/app-page.runtime.dev.js:42:697
    at AsyncLocalStorage.run (node:async_hooks:346:14)
    at Object.wrap (/Users/olafsiebert/html/myelementalbeing/node_modules/next/dist/compiled/next-server/app-page.runtime.dev.js:36:16575)
    at /Users/olafsiebert/html/myelementalbeing/node_modules/next/dist/compiled/next-server/app-page.runtime.dev.js:42:616
    at AsyncLocalStorage.run (node:async_hooks:346:14)
    at Object.wrap (/Users/olafsiebert/html/myelementalbeing/node_modules/next/dist/compiled/next-server/app-page.runtime.dev.js:36:15721)
    at r4 (/Users/olafsiebert/html/myelementalbeing/node_modules/next/dist/compiled/next-server/app-page.runtime.dev.js:42:543)
    at nf.render (/Users/olafsiebert/html/myelementalbeing/node_modules/next/dist/compiled/next-server/app-page.runtime.dev.js:42:4608)
    at doRender (/Users/olafsiebert/html/myelementalbeing/node_modules/next/dist/server/base-server.js:1433:44)
    at cacheEntry.responseCache.get.routeKind (/Users/olafsiebert/html/myelementalbeing/node_modules/next/dist/server/base-server.js:1594:34)
    at ResponseCache.get (/Users/olafsiebert/html/myelementalbeing/node_modules/next/dist/server/response-cache/index.js:49:26)
    at DevServer.renderToResponseWithComponentsImpl (/Users/olafsiebert/html/myelementalbeing/node_modules/next/dist/server/base-server.js:1502:53)
    at /Users/olafsiebert/html/myelementalbeing/node_modules/next/dist/server/base-server.js:997:121
    at NextTracerImpl.trace (/Users/olafsiebert/html/myelementalbeing/node_modules/next/dist/server/lib/trace/tracer.js:105:20)
    at DevServer.renderToResponseWithComponents (/Users/olafsiebert/html/myelementalbeing/node_modules/next/dist/server/base-server.js:997:41)
    at DevServer.renderPageComponent (/Users/olafsiebert/html/myelementalbeing/node_modules/next/dist/server/base-server.js:1919:35)
    at async DevServer.renderToResponseImpl (/Users/olafsiebert/html/myelementalbeing/node_modules/next/dist/server/base-server.js:1957:32)
    at async DevServer.pipeImpl (/Users/olafsiebert/html/myelementalbeing/node_modules/next/dist/server/base-server.js:915:25)
    at async NextNodeServer.handleCatchallRenderRequest (/Users/olafsiebert/html/myelementalbeing/node_modules/next/dist/server/next-server.js:272:17)
    at async DevServer.handleRequestImpl (/Users/olafsiebert/html/myelementalbeing/node_modules/next/dist/server/base-server.js:811:17)
    at async /Users/olafsiebert/html/myelementalbeing/node_modules/next/dist/server/dev/next-dev-server.js:339:20
    at async Span.traceAsyncFn (/Users/olafsiebert/html/myelementalbeing/node_modules/next/dist/trace/trace.js:154:20)
    at async DevServer.handleRequest (/Users/olafsiebert/html/myelementalbeing/node_modules/next/dist/server/dev/next-dev-server.js:336:24)
    at async invokeRender (/Users/olafsiebert/html/myelementalbeing/node_modules/next/dist/server/lib/router-server.js:174:21)
    at async handleRequest (/Users/olafsiebert/html/myelementalbeing/node_modules/next/dist/server/lib/router-server.js:353:24)
    at async requestHandlerImpl (/Users/olafsiebert/html/myelementalbeing/node_modules/next/dist/server/lib/router-server.js:377:13)
    at async Server.requestListener (/Users/olafsiebert/html/myelementalbeing/node_modules/next/dist/server/lib/start-server.js:141:13)

My root layout contains the hidden forms:

    return (<>
      <Header />
      {children}
      <Footer />

      <form name="contact" data-netlify="true" hidden>
        <input type="text" name="name" />
        <input type="email" name="email" />
        <input type="enquiryType" name="enquiryType" />
        <input type="agreeToTerms" name="agreeToTerms" />
        <textarea name="message" />
      </form>

      <form name="review" data-netlify="true" hidden>
        <input type="text" name="name" />
        <input type="email" name="email" />
        <input type="product_id" name="product_id" />
        <input type="product_type" name="product_type" />
        <input type="agreeToTerms" name="agreeToTerms" />
        <textarea name="reviewBody" />
      </form>
      </>
    )

  }

You cannot post to /:

Post to a static file (like favicon.ico).

I followed the instructions from the documentation:

Ajax submit form. Here is another example using Nextjs13

Did you try what I suggested above?

Yes, no errors but no submissions captured. I decided to go with Nodemailer instead.

@nolafs (or anyone else encountering this thread in future), this may help:

1 Like

Thanks, this worked!!!

glad to hear it! thanks for writing back in and sharing this.