FormThunder isn't open yet. This site describes the service as it will run at launch. Find out more

FormThunder
Menu

Submit a JavaScript contact form with fetch and stay on the page

To send a contact form with plain JavaScript, stop the default submit, turn the fields into JSON and POST them to FormThunder with fetch. The response tells you whether it worked, so you can show a message on the same page. If the script doesn't load, the form still posts normally, because the action URL stays on the form.

Updated

The JavaScript code

contact.html
<form action="https://formthunder.com/submit" method="POST" id="contact">
  <input type="hidden" name="access_key" value="YOUR_ACCESS_KEY">
  <input type="hidden" name="subject" value="New message from the website">
  <input type="checkbox" name="botcheck" style="display:none" tabindex="-1" autocomplete="off">

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

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

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

  <button type="submit">Send</button>
</form>
<p id="result" role="status"></p>
contact.js
const form = document.getElementById("contact");
const result = document.getElementById("result");

form.addEventListener("submit", async (event) => {
  event.preventDefault();
  result.textContent = "Sending...";
  try {
    const response = await fetch(form.action, {
      method: "POST",
      headers: { "Content-Type": "application/json", Accept: "application/json" },
      body: JSON.stringify(Object.fromEntries(new FormData(form))),
    });
    const data = await response.json();
    result.textContent = data.message;
    if (data.success) form.reset();
  } catch {
    result.textContent = "We couldn't send that. Please try again.";
  }
});

Replace YOUR_ACCESS_KEY with your free key. It's public by design, so it's safe in client code.

Notes for JavaScript

  • CORS is open on /submit, so this works from any origin, and that includes a file opened from localhost.
  • Send JSON or FormData. Use FormData directly, without a Content-Type header, if the form has a file input.
  • A 429 means this IP sent too many requests to this form in a minute. Show the message and let the visitor retry.
  • Multiple checkboxes with one name need formData.getAll(name), because Object.fromEntries keeps only the last value.

Or have your AI add it

Connect the MCP server, then ask. The agent creates the form and writes the code for you, and it can send a real test when it's done.

You, to your AI
Add a FormThunder contact form to this JavaScript site that emails hello@example.com. Send a test when you're done.

Questions

What does the response look like?

JSON with success, message, submission_id and body. On failure, success is false and message explains why, with an HTTP status such as 400, 403, 413 or 429.

Do I need jQuery or a library?

No. fetch and FormData are built into every current browser.

Get your JavaScript form sending

The free plan covers 250 submissions a month, with MCP, the CLI and the brand kit included.