FreelyForm

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

ShapeAddsRemoves
Client fetch to the endpointOne handlerThe server route, the secret, the deploy coupling
Route handler that forwardsA server file, a hop, a failure modeNothing it needs
Server ActionA server runtime in the form pathNothing 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.

app/contact/form.tsx — the small version
"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.

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