Next.js contact form
Next.js gives you two ways to handle a contact form: post directly from a Client Component, or proxy through a Route Handler. The direct route needs no server code at all — a "use client" component sends the fields to TruForms and updates state with the result.
Reach for an app/api/contact/route.ts Route Handler only when you want the submission to stay server-side — to attach a server-only secret, run extra validation, or keep the request out of the browser’s network tab. Otherwise the client-side post is simpler and one less moving part, since the access key is public and only identifies the form.
Copy-paste snippet
'use client';
import { FormEvent, useState } from 'react';
export function ContactForm() {
const [status, setStatus] = useState<'idle' | 'sending' | 'sent' | 'error'>('idle');
async function onSubmit(e: FormEvent<HTMLFormElement>) {
e.preventDefault();
const form = e.currentTarget;
setStatus('sending');
const body = new FormData(form);
body.append('access_key', 'YOUR_ACCESS_KEY');
const res = await fetch('https://truforms.truenotech.com/api/submit', { method: 'POST', body });
const result = await res.json();
setStatus(result.success ? 'sent' : 'error');
if (result.success) form.reset();
}
return (
<form onSubmit={onSubmit}>
<input name="name" required />
<input name="email" type="email" required />
<textarea name="message" required />
<input name="botcheck" type="text" style={{ display: 'none' }} />
<button disabled={status === 'sending'}>Send</button>
</form>
);
}Replace YOUR_ACCESS_KEY with your form's access key from the dashboard. The access key is public — it only identifies the form.
How it works
- 1
Create a form in the TruForms dashboard and copy its access key.
- 2
Add the client component (note the "use client" directive) and replace YOUR_ACCESS_KEY.
- 3
No backend needed — but you can proxy through a Route Handler if you prefer to keep the call server-side.
Notes for Next.js
- The `"use client"` directive is required — `fetch` plus `useState` only run in a Client Component, not a Server Component.
- You don’t need a Route Handler. Add `app/api/contact/route.ts` only if you want to keep the request server-side or attach custom logic, then POST to it instead.
- Same caveat as React: grab `const form = e.currentTarget` before the await so `form.reset()` doesn’t hit a null after the request.