Forms — Kalme Next.js
The Contact form works as soon as you run the site: it checks the required fields, shows a sending state, says “Thank you”, keeps what the visitor typed when something fails, and drops spam with a hidden honeypot field.
Out of the box, submissions are written to the server log (FORM_PROVIDER=console) — fine while you build, useless
once you publish. Pick a destination before launch.
The quickest way to receive submissions
Section titled “The quickest way to receive submissions”-
Copy the example settings:
Terminal window cp .env.example .env.local -
Choose one:
You want Set An email for every submission FORM_PROVIDER=resend+RESEND_API_KEY,FORM_EMAIL_TO,FORM_EMAIL_FROMIt in your CRM, Slack, a spreadsheet… FORM_PROVIDER=webhook+FORM_WEBHOOK_URL(Zapier, Make, n8n, Formspree, your own API)The sender in a Mailchimp audience FORM_PROVIDER=mailchimp+MAILCHIMP_API_KEY,MAILCHIMP_AUDIENCE_IDSeveral at once work:
FORM_PROVIDER=resend,mailchimp. -
Restart
npm run devand send a test submission. -
Put the same variables in your host’s dashboard before deploying —
.env.localis not uploaded.
Details for each provider, the webhook payload, Mailchimp consent settings and how to write your own provider: Forms reference.
What the form asks for
Section titled “What the form asks for”| Field | Name it arrives under | Required |
|---|---|---|
| First Name | firstName |
yes |
| Last Name | lastName |
|
email |
yes | |
| Phone Number | phone |
|
| Services (one choice) | service |
|
| Message | message |
The service choices are the titles of your services (src/content/collections/services.ts), so a new service appears
in the form by itself. Resend puts the sender’s first and last name in the email’s subject, and Mailchimp stores them as
FNAME and LNAME.
The labels, placeholders, button texts (“Submit”, “Thank you”, “Something went wrong”) and the notification title are
content: form in src/content/pages/contact.ts, with each field’s name, label and placeholder. A field arrives at
your provider under its name, so keep the names you already map somewhere.
What the form sends is decided in submitContact in src/lib/actions.ts: it lists the fields and which are required.
To add a field, add it in three places — the page content (its label), the form (src/components/sections/ContactSection.tsx)
and the list in actions.ts. To make a field required, add required to it in ContactSection.tsx (the browser checks
it first) and its name to required in actions.ts.
Testing before launch
Section titled “Testing before launch”- Submit the form on your own site after deploying, with a real address.
- Check the destination (inbox, spreadsheet, audience) and reply to the test.
- Try a failure on purpose: set a wrong API key, submit, and confirm the visitor sees “Something went wrong” and their text is still there.
The honeypot stops the usual bots: a bot that fills the hidden field is shown “Thank you” and nothing is sent. If you start getting spam anyway, put a service in front through the webhook provider (Formspree, Web3Forms and Basin all include spam filtering), or add a captcha to the form component.
Privacy
Section titled “Privacy”Submissions go only where you point them; nothing is sent anywhere else. Most countries expect you to say what
happens to the data people send you, so add a line under the form (its copy is in src/content/pages/contact.ts)
linking to the Privacy Policy — its text is in src/content/collections/legal.ts and is a demo to replace with your own.
Keep a consent checkbox there if you plan to add senders to a marketing audience.