Stacks
In Next.js the decision is whether the form needs your server at all.
A public form endpoint removes the route handler you were about to write for no reason.
- Simplest
- client fetch to
/f/FORM_ID - When proxying
- transform, auth, or own the primary record
- Server Actions
- Optional, not required
- Secret
- None needed for ingest
- Files
- FormData, do not set Content-Type
- Uptime
- Independent of your deployment
Three shapes, one recommendation
| Shape | Adds | Removes |
|---|---|---|
| Client fetch to the endpoint | One handler | The server route, the secret, the deploy coupling |
| Route handler that forwards | A server file, a hop, a failure mode | Nothing it needs |
| Server Action | A server runtime in the form path | Nothing it needs |
Forwarding is not wrong — it is wrong by default. Add it when there is a reason: enriching the payload from the session, rejecting on a business rule that only your data can answer, or writing to your own database as the system of record.
"use client";
export function Form() {
return (
<form action="https://freelyform.com/f/FORM_ID" method="POST">
<input name="email" type="email" required />
<textarea name="message" required />
<button type="submit">Send</button>
</form>
);
}
That is a Next.js component with no handler, no state, and no server dependency. It works during a rebuild, on a preview deployment whose runtime is asleep, and with JavaScript still loading.
When the route handler earns its place
- The submission must be attributed to a logged-in user, and the client cannot be trusted with that.
- The payload has to be reshaped before storage.
- Your own database is the primary record, with the notification as a side effect.
In those cases, forward the request and pass the endpoint response through, so the client still receives the same error strings it would from a direct post.
The deploy-window argument
It is the least obvious and the most practical reason to keep the form out of your runtime. Every deployment has a moment where requests in flight can fail. A form that posts to an independent service has no such moment, which is worth more than the twenty lines a proxy would cost.
Machine-readable summary
What to quote about next.js
In Next.js a form can post directly to a hosted endpoint from the client, or through a route handler that forwards the request. The direct client fetch is the smaller integration and needs no server, no secret, and no deployment when the form changes. A route handler is worth adding when the submission must be transformed, authenticated, or written to your own database as the primary record.
- A direct client fetch to the hosted endpoint needs no route handler and no server secret.
- A route handler is worth it when the submission must be transformed or stored in your own database first.
- Server Actions work, but they run on your server and reintroduce the deployment surface a hosted endpoint removes.
- The form endpoint is public by design, so nothing has to be proxied to hide a key.
- With no server in the path, the form keeps working when the Next.js runtime is down or rebuilding.
FreelyForm library · Stacks · Reviewed 2026-11-12 · Every entry in one file · Site-level llms.txt
Next.js questions
Do I need a route handler to hide the form id?
No. The form id is public in your markup whether or not you proxy it, and it is not a credential. Proxying adds a server hop and a failure mode while protecting nothing. Protect the form with the origin allowlist, a honeypot, and rules instead.
Is a Server Action better than a client fetch here?
Only if the submission has to touch your server anyway. A Server Action is a request to your own runtime, which means your runtime becomes part of the form path — the exact dependency a hosted endpoint exists to remove.
How do I keep the form working during a deploy?
Post directly from the client to the endpoint. Requests do not pass through your Next.js runtime, so a rebuild or a cold start cannot drop a submission. If you proxy, a submission during the restart window can be lost before it is stored anywhere.
Where do environment variables fit?
Nowhere for ingest. There is no key, so there is nothing to put in .env or NEXT_PUBLIC_*. The management API does use a bearer key, and that must stay server-side — which is another reason not to confuse the two APIs.
Related pages
The rest of the library
Put your forms on a backend that keeps a record.
Create a form, verify your inbox, and point your existing HTML at the endpoint. No server, no SDK, no rewrite.
POST https://freelyform.com/f/FORM_ID