Next js to Contact Form 7 with Turnstile

Using Wordpress as a headless CMS for a Next JS frontend and allowing form submissions via Contact Form 7

Enable the Turnstile integration in Wordpress admin at /wp-admin/admin.php?page=wpcf7-integration using your credentials obtained from the Cloudflare Turnstile dashboard https://dash.cloudflare.com/.

Configure theses environment variables

# .env.local
NEXT_PUBLIC_WORDPRESS_URL=https://url.to.wordpress
NEXT_PUBLIC_TURNSTILE_SITE_KEY=
WORDPRESS_USERNAME=
WORDPRESS_APPLICATION_PASSWORD=

In your Next js app install this excellent React Turnstile component: npm i @marsidev/react-turnstile

From your form component, submit the form data to your API route handler at /api/contact-form-7/[form id]

// app/components/form.tsx
// ...
const turnstileRef = useRef<TurnstileInstance>(null)

const handleSubmit = async (
  values: Record<string, string>,
  form: FormikHelpers<Record<string, string>>
) => {
  try {
    const _wpcf7_turnstile_response = turnstileRef.current?.getResponse()
    const response = await fetch(`/api/contact-form-7/${id}`, {
      method: 'post',
      body: JSON.stringify({
        ...values,
        _wpcf7_turnstile_response
      })
    })
    // ...
  }
}
return (<Formik initialValues={initialValues} onSubmit={handleSubmit}>
    {(props) => (
      <Form>
        // ... your form fields
          {process.env.NEXT_PUBLIC_TURNSTILE_SITE_KEY && (
            <Turnstile
              ref={turnstileRef}
              siteKey={process.env.NEXT_PUBLIC_TURNSTILE_SITE_KEY}
            />
          )}
          <button type="submit">Submit</button>
        </Box>
      </Form>
    )}
  </Formik>)

Then in your route handler, submit the form contents as form data to your Wordpress REST endpoint.

// app/api/contact-form-7/[id]/route.ts

export interface IContactForm7FeedbackResponse {
  contact_form_id: number;
  status: "validation_failed" | "mail_sent" | "spam";
  message?: string;
}

export async function POST(
  request: NextRequest,
  { params }: { params: Promise<{ id: string }> }
) {
  const { id } = await params;
  const json = await request.json();

  const body = new FormData();
  for (const key in json)
    if (Array.isArray(json[key]))
      for (let i = 0; i < json[key].length; i++)
        body.append(`${key}[]`, json[key][i]);
    else body.append(key, json[key]);

  body.append("_wpcf7_unit_tag", id);

  try {
    const res = await fetch(
      `${process.env.NEXT_PUBLIC_WORDPRESS_URL}/wp-json/contact-form-7/v1/contact-forms/${id}/feedback`,
      {
        method: "POST",
        body,
        headers: {
          Authorization:
            "Basic " +
            Buffer.from(
              `${process.env.WORDPRESS_USERNAME}:${process.env.WORDPRESS_APPLICATION_PASSWORD}`,
              "binary"
            ).toString("base64"),
        },
      }
    );
    const ret: IContactForm7FeedbackResponse = await res.json();
    if (res.ok && ret.status === "mail_sent")
      return NextResponse.json(ret, { status: 200 });
    else return NextResponse.json(ret, { status: 400 });
  } catch (e) {
    console.log("e", e);

    return NextResponse.json(
      { success: false, message: "Internal Server Error" },
      { status: 500 }
    );
  }
}

Add a comment