Vercel

Add a contact form to a static site on Vercel with no serverless function to write or pay for.

On Vercel the usual way to handle a form is a Vercel Function that receives the post and calls an email API. With SilentForms you skip that layer entirely: the form posts from the visitor's browser to the submit endpoint, so the project stays a static deployment with no function code, no email provider credentials and no function invocations. It works the same for a hand-written HTML site and for a framework build.

1. Get an access key#

Enter your email on the access key page and click the confirmation link. The key only routes submissions to your inbox, so it can sit in public HTML or a client bundle.

2a. Plain HTML project#

For a site without a build step, put the key directly in the form. Create contact.html:

<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>Contact</title>
</head>
<body>
  <h1>Contact</h1>
  <form action="https://silentforms.com/api/submit" method="POST">
    <input type="hidden" name="accessKey" value="YOUR_ACCESS_KEY_HERE">
    <input type="hidden" name="redirect" value="https://example.com/thanks">

    <label for="name">Name</label>
    <input id="name" type="text" name="name" autocomplete="name" required>

    <label for="email">Email</label>
    <input id="email" type="email" name="email" autocomplete="email" required>

    <label for="message">Message</label>
    <textarea id="message" name="message" rows="6" required></textarea>

    <!-- Honeypot: hidden from people, filled in by bots -->
    <input type="checkbox" name="botcheck" style="display: none;" tabindex="-1" autocomplete="off">

    <button type="submit">Send</button>
  </form>
</body>
</html>

Add a thanks.html page next to it. To serve both at /contact and /thanks without the .html extension, add a vercel.json to the project root:

{
  "cleanUrls": true
}

With cleanUrls, thanks.html is served at /thanks, and /thanks.html redirects there. That is why the redirect above has no extension. Replace example.com with your production domain; the field must be an absolute URL. Without it, visitors land on the default SilentForms success page. See Redirect after submit.

2b. Framework project#

In a framework you keep the key in an environment variable instead of the source. Add it in the Vercel dashboard: select the project, open Environment Variables in the project settings, enter the name and value, choose the environments (Production, Preview, Development) and save. Use the prefix your framework needs to expose a variable to browser code:

FrameworkVariable nameRead it with
Next.jsNEXT_PUBLIC_SILENTFORMS_KEYprocess.env.NEXT_PUBLIC_SILENTFORMS_KEY
Vite (React, Vue, Svelte)VITE_SILENTFORMS_KEYimport.meta.env.VITE_SILENTFORMS_KEY
AstroPUBLIC_SILENTFORMS_KEYimport.meta.env.PUBLIC_SILENTFORMS_KEY
SvelteKitPUBLIC_SILENTFORMS_KEYimport { PUBLIC_SILENTFORMS_KEY } from '$env/static/public'
GatsbyGATSBY_SILENTFORMS_KEYprocess.env.GATSBY_SILENTFORMS_KEY

These prefixed values are inlined into the JavaScript at build time. Vercel applies variable changes only to new deployments, so redeploy after adding or changing one. For local development, put the same line in .env.local, or run vercel env pull to download the Development values.

The form component itself is the same as on any other host. Follow the guide for your framework: Next.js, React, Vue, SvelteKit or Astro.

What you do not need#

  • No api/ route or Vercel Function. The browser talks to SilentForms directly, and SilentForms answers the cross-origin request, including the CORS preflight that fetch with JSON sends.
  • No email provider or SMTP secrets in your project settings.
  • No server for Next.js. A Next.js app with output: 'export' deploys to Vercel as static files and the form keeps working.

Test it#

Push to a branch to get a preview deployment, open the contact page on its URL and send yourself a message. Then check:

  • The browser's network tab shows the submit request going to the SilentForms endpoint.
  • In a framework build, view the page source or bundle and confirm the access key is there. An empty value means the variable was missing for that environment when the deployment built.
  • The email arrives, and with redirect you land on your thank-you page.

Previews and domain restriction#

Preview deployments get their own vercel.app URLs. If you restrict the form to your domain in the dashboard, submissions from previews are refused with "This form does not accept submissions from this domain." Either test on production, or add your project's preview hostname as well. Do not add vercel.app on its own: a bare domain covers all its subdomains, so it would allow every Vercel project. See Restrict to your domain.

Also note that the redirect in a plain HTML form points at production, so a preview submission sends you to the production thank-you page. That is expected.

Gotchas#

  • "Access key is required." The variable is not set for the environment you deployed (Preview and Production are separate), or the prefix is wrong.
  • /thanks returns 404. cleanUrls is not set, so the page lives at /thanks.html. Either add vercel.json or put the extension in redirect.
  • The value is right locally but empty on Vercel. .env.local is not deployed. Set the variable in the project settings and redeploy.

For every error message, see Troubleshooting.

Next steps#