DEV Community

snapform
snapform

Posted on

How to Add a Contact Form to Any Static Website (Without a Backend)

Static sites are fast, cheap, and easy to deploy. But they all hit the same wall: forms.

Without a backend, you can't process form submissions. No PHP, no Node server, no database. So how do you add a working contact form to a static site hosted on Vercel, Netlify, GitHub Pages, or anywhere else?

In this guide, I'll show you 3 approaches — from the simplest (2 minutes) to fully custom.

Option 1: HTML Form with a Form Backend (Easiest)

The fastest way. You point your HTML form to a third-party endpoint that handles everything — storing submissions, sending email notifications, and spam protection.

Here's a complete working example using SnapForm:

Step 1: Create a free account

Sign up at snapform.cc and create a new form. You'll get a unique endpoint URL.

Step 2: Add the form to your site

<form action="https://snapform.cc/api/f/YOUR_FORM_ID" method="POST">
    <label for="name">Name</label>
    <input type="text" name="name" id="name" required />

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

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

    <!-- Honeypot spam protection (keep this hidden) -->
    <input type="text" name="_gotcha" style="display:none" tabindex="-1" autocomplete="off" />

    <button type="submit">Send Message</button>
</form>
Enter fullscreen mode Exit fullscreen mode

That's it. No JavaScript, no build step, no dependencies.

Step 3: Check your dashboard

Every submission shows up in your SnapForm dashboard with email notifications.

What you get for free:

  • Email notifications
  • File uploads (most services charge for this)
  • Spam protection (honeypot, no CAPTCHA)
  • CSV export

Option 2: AJAX Submission (For SPAs & Custom UX)

If you're using React, Vue, Astro, or any JavaScript framework, you probably want to handle the submission without a page redirect.

const form = document.querySelector('#contact-form');

form.addEventListener('submit', async (e) => {
    e.preventDefault();

    const formData = new FormData(form);
    const data = Object.fromEntries(formData);

    try {
      const res = await fetch('https://snapform.cc/api/f/YOUR_FORM_ID', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify(data),
      });

      if (res.ok) {
        form.reset();
        alert('Message sent!');
      } else {
        alert('Something went wrong. Please try again.');
      }
    } catch (err) {
      alert('Network error. Please try again.');
    }
});
Enter fullscreen mode Exit fullscreen mode

This works from any domain — CORS is fully supported.

React Example

function ContactForm() {
    const [status, setStatus] = useState('idle');

    async function handleSubmit(e) {
      e.preventDefault();
      setStatus('sending');

      const data = Object.fromEntries(new FormData(e.target));

      const res = await fetch('https://snapform.cc/api/f/YOUR_FORM_ID', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify(data),
      });

      setStatus(res.ok ? 'sent' : 'error');
    }

    if (status === 'sent') return <p>Thanks! Message received.</p>;

    return (
      <form onSubmit={handleSubmit}>
        <input name="name" placeholder="Name" required />
        <input name="email" type="email" placeholder="Email" required />
        <textarea name="message" placeholder="Message" required />
        <button type="submit" disabled={status === 'sending'}>
          {status === 'sending' ? 'Sending...' : 'Send'}
        </button>
      </form>
    );
  }
Enter fullscreen mode Exit fullscreen mode

Option 3: With File Uploads

Need users to attach a resume, screenshot, or document? Just add enctype="multipart/form-data" and a file input:

 <form action="https://snapform.cc/api/f/YOUR_FORM_ID"
        method="POST" enctype="multipart/form-data">

    <input type="text" name="name" placeholder="Name" required />
    <input type="email" name="email" placeholder="Email" required />

    <label for="resume">Attach your resume</label>
    <input type="file" name="resume" id="resume" />

    <input type="text" name="_gotcha" style="display:none" tabindex="-1" autocomplete="off" />

    <button type="submit">Submit</button>
</form>
Enter fullscreen mode Exit fullscreen mode

Supported file types: JPEG, PNG, GIF, PDF, DOC/DOCX, XLS/XLSX, TXT, CSV, ZIP.

File upload with JavaScript:

const form = document.querySelector('form');
  const formData = new FormData(form);

  // Don't set Content-Type — the browser sets it automatically
  const res = await fetch('https://snapform.cc/api/f/YOUR_FORM_ID', {
    method: 'POST',
    body: formData,
});
Enter fullscreen mode Exit fullscreen mode

Tip: When uploading files via fetch, do NOT set the Content-Type header manually. The browser needs to set the multipart/form-data boundary automatically.

Adding Webhooks (Optional)

Want submissions sent to Slack, Zapier, or your own API? Add a webhook URL in your SnapForm dashboard. Every submission triggers a POST request:

{
    "formId": "your-form-id",
    "formName": "Contact Form",
    "submissionId": "abc123",
    "data": {
      "name": "Jane Doe",
      "email": "jane@example.com",
      "message": "Hello!"
    },
    "createdAt": "2026-02-28T10:30:00.000Z"
}
Enter fullscreen mode Exit fullscreen mode

Querying Submissions via API

If you're on a Pro or Business plan, you can query your data programmatically:

# List all forms
curl -H "Authorization: Bearer sk_live_xxx" \
    https://snapform.cc/api/v1/forms

# Get submissions with pagination
curl -H "Authorization: Bearer sk_live_xxx" \
    "https://snapform.cc/api/v1/forms/FORM_ID/submissions?page=1&limit=10"
Enter fullscreen mode Exit fullscreen mode

Useful for building custom dashboards, syncing to a CRM, or exporting data on a schedule.

Quick Comparison: Form Backend Options

Free Plan Paid File Uploads (Free)
SnapForm 50 subs/mo $9/mo for 2,000 Yes
Formspree 50 subs/mo $24/mo for 1,000 No
Getform 50 subs/mo $16/mo for 1,000 No
FormSubmit Unlimited Free No

Summary

For most static sites, Option 1 (plain HTML + form backend) is all you need. It works everywhere — GitHub Pages, Vercel, Netlify, Cloudflare Pages, or even a raw HTML file.

No backend. No JavaScript (unless you want it). No CAPTCHA. Just a form that works.

Links:

  • SnapForm — Free signup, no credit card
  • Documentation
  • Full API Reference

What form solution are you using for your static sites? Let me know in the comments!

Top comments (0)