Quick Start

FreeForms lets you collect form submissions from any HTML page without writing server-side code. Here's what you need:

  1. Create a free account at FreeForms
  2. Create a new form in the dashboard to get your unique endpoint ID
  3. Add the endpoint URL as your form's action attribute
  4. Submit the form — it works immediately!
contact.html
<form action="https://YOUR-APP.web.app/f/YOUR-FORM-ID" method="POST">
  <input type="email" name="email" placeholder="Your email" required />
  <textarea name="message" placeholder="Your message"></textarea>
  <button type="submit">Send Message</button>
</form>

Account Setup

After signing up, go to your Dashboard and click + New Form.

Each form gets a unique ID. Your submission endpoint will be:

POST https://YOUR-APP.web.app/f/{formId}

Copy this URL — you'll use it as the action attribute on your HTML form, or as the URL in your fetch() call.

HTML Forms

The simplest integration — no JavaScript needed. The form will redirect to a thank-you page after submission.

<form action="https://YOUR-APP.web.app/f/YOUR-FORM-ID" method="POST">

  <!-- Optional: redirect to your own page after submission -->
  <input type="hidden" name="_next" value="https://yoursite.com/thanks.html" />

  <!-- Optional: custom email subject -->
  <input type="hidden" name="_subject" value="New contact message!" />

  <!-- Spam protection: keep this hidden! -->
  <input type="text" name="_honeypot" style="display:none" tabindex="-1" autocomplete="off" />

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

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

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

Every input field in your form will be captured. Use any name attributes — they'll appear as columns in your dashboard.

AJAX / fetch()

For a better UX without page reloads, use the Fetch API. Set the Accept: application/json header to get a JSON response instead of a redirect.

async function handleSubmit(event) {
  event.preventDefault();
  const form   = event.target;
  const button = form.querySelector('[type="submit"]');

  button.disabled    = true;
  button.textContent = 'Sending…';

  try {
    const response = await fetch(
      'https://YOUR-APP.web.app/f/YOUR-FORM-ID',
      {
        method: 'POST',
        headers: { 'Accept': 'application/json' },
        body: new FormData(form)
      }
    );

    if (response.ok) {
      const json = await response.json();
      // json = { ok: true, message: "Submission received." }
      form.innerHTML = '<p>Thanks! We\'ll be in touch.</p>';
    } else {
      throw new Error('Submission failed');
    }
  } catch (err) {
    alert('Something went wrong. Please try again.');
    button.disabled    = false;
    button.textContent = 'Send';
  }
}

document.querySelector('form').addEventListener('submit', handleSubmit);
JSON Response: When you include Accept: application/json, the endpoint returns {"ok": true, "message": "Submission received."} instead of redirecting.

Special Fields

Add hidden inputs with these special names to control behavior. They won't appear in your submission data.

FieldTypeDescription
_nextURLRedirect URL after a successful submission (overrides dashboard setting).
_subjectStringSubject line for the notification email.
_replytoEmailSets the Reply-To header so you can reply directly to the submitter.
_ccEmailCC this email on every notification.
_honeypotStringSpam trap — any submission with this field filled is silently discarded.

Spam Protection

FreeForms uses a honeypot field technique. Add a hidden text input named _honeypot (or your custom name from Settings). Bots that auto-fill all fields will trigger this trap and the submission is silently dropped.

<!-- Add this to any form. Keep it invisible to humans! -->
<input
  type="text"
  name="_honeypot"
  style="display:none !important"
  tabindex="-1"
  autocomplete="off"
/>
Tip: You can change the honeypot field name in your Form's Settings tab. This makes it slightly harder for sophisticated bots to detect.

Email Notifications

FreeForms sends you an email every time your form is submitted. Configure the notification email address in your Form Settings.

Self-Hosting Note: Email requires a Firebase Blaze plan (pay-as-you-go, very cheap) and SMTP configuration. Set these Firebase Function env vars:
firebase functions:config:set \
  smtp.host="smtp.gmail.com" \
  smtp.port="587" \
  smtp.user="you@gmail.com" \
  smtp.pass="your-gmail-app-password" \
  smtp.from="you@gmail.com"

For Gmail, create an App Password at myaccount.google.com/apppasswords. Other SMTP providers like SendGrid, Mailgun, etc. also work.

Export Data

Download all submissions for a form from the Submissions tab in the dashboard.

  • CSV — Opens in Excel, Numbers, or Google Sheets
  • JSON — Machine-readable, includes all metadata

Exports include submission date, read status, and all form field data.

Self Hosting

FreeForms is 100% open source (MIT). You can run it on your own Firebase project.

Prerequisites

  • Node.js 18+
  • Firebase CLI: npm install -g firebase-tools
  • A Firebase project (Blaze plan for email notifications)

Setup Steps

# 1. Clone the repository
git clone https://github.com/apssmandal/FreeForms.git
cd freeforms

# 2. Install Cloud Function dependencies
cd functions && npm install && cd ..

# 3. Edit your Firebase config
# → Open public/js/firebase-config.js
# → Replace placeholder values with your Firebase project credentials
# → Open .firebaserc and replace YOUR_FIREBASE_PROJECT_ID

# 4. Enable Authentication in Firebase Console
# → Go to Authentication → Sign-in method
# → Enable Email/Password and Google

Deploy

# Login to Firebase
firebase login

# Set SMTP config (optional but needed for email)
firebase functions:config:set \
  smtp.host="smtp.gmail.com" \
  smtp.port="587" \
  smtp.user="you@gmail.com" \
  smtp.pass="your-app-password"

# Deploy everything
firebase deploy

# Or deploy independently:
firebase deploy --only hosting     # Frontend only
firebase deploy --only functions   # Backend only
firebase deploy --only firestore   # Rules + indexes only

Your app will be live at https://YOUR-PROJECT-ID.web.app