Webform

Using webforms with Next.js and Drupal


There are two ways to handle Webform in Next.js for Drupal.

  1. Client Side
  2. Server Side (API Route)

Both implementations use the Webform REST module.


Demo

See https://example-webform.next-drupal.org.


Installation

  1. Install the Webform REST module: composer require drupal/webform_rest
  2. Visit /admin/config/services/rest and enable the Webform Submit resource.
  3. Select POST for the Methods, json for Accepted request formats and cookie for Authentication providers
  4. Save configuration
  5. Next, visit /admin/people/permissions and give anonymous users the Access POST on Webform Submit resource permission.

Client Side

We use the Webform REST module to submit the form values directly to Drupal.

Note: when submitting webform values client-side, the webform_id and the Drupal base URL is exposed.

export default function ContactPage() {
async function handleSubmit(event) {
event.preventDefault()
const response = await fetch(
`${process.env.NEXT_PUBLIC_DRUPAL_BASE_URL}/webform_rest/submit`,
{
method: "POST",
body: JSON.stringify({
webform_id: "contact",
name: event.target.name.value,
email: event.target.email.value,
}),
headers: {
"Content-Type": "application/json",
},
}
)
if (response.ok) {
// Show success.
}
// Handle error.
}
return (
<div>
<form onSubmit={handleSubmit}>
<p>Implement your form here</p>
<div>
<label htmlFor="name">Name</label>
<input type="text" id="name" name="name" />
</div>
<div>
<label htmlFor="email">Email</label>
<input type="email" id="email" name="email" />
</div>
<button type="submit">Submit</button>
</form>
</div>
)
}

Server side

We submit the form values to a custom API route first. The API route then submits the form to Drupal using the Webform REST module.

This is useful if we need to hide client IDs and secrets or our Drupal implementation.

  1. Create a /pages/api/contact API route to handle our POST request.

pages/api/contact.ts

import { NextApiRequest, NextApiResponse } from "next"
import { drupal } from "lib/drupal"
export default async function handler(
request: NextApiRequest,
response: NextApiResponse
) {
try {
if (request.method === "POST") {
const url = drupal.buildUrl("/webform_rest/submit")
// Submit to Drupal.
const result = await drupal.fetch(url.toString(), {
method: "POST",
body: JSON.stringify({
webform_id: "contact",
name: request.body.name,
email: request.body.email,
}),
headers: {
"Content-Type": "application/json",
},
})
if (!result.ok) {
throw new Error()
}
response.status(200).end()
}
} catch (error) {
return response.status(400).json(error.message)
}
}
  1. Next, update our contact form to submit to this API route.
export default function ContactPage() {
async function handleSubmit(event) {
event.preventDefault()
const response = await fetch(`/api/contact`, {
method: "POST",
body: JSON.stringify({
name: event.target.name.value,
email: event.target.email.value,
}),
})
if (response.ok) {
// Show success.
}
// Handle error.
}
return (
<div>
<form onSubmit={handleSubmit}>
<p>Implement your form here</p>
<div>
<label htmlFor="name">Name</label>
<input type="text" id="name" name="name" />
</div>
<div>
<label htmlFor="email">Email</label>
<input type="email" id="email" name="email" />
</div>
<button type="submit">Submit</button>
</form>
</div>
)
}

Example

See example-webform for a real-life example of building webform with react-hook-form and handling validation using yup.