LeadGen.js

Zero-dependency JavaScript library for capturing leads directly to Google Sheets

MIT License Zero Dependencies Vanilla JS No Backend

Table of Contents

About LeadGen.js

LeadGen.js is a lightweight, zero-dependency vanilla JavaScript library designed for frontend developers who need to capture form submissions and store lead data directly in Google Sheets without setting up any backend infrastructure.

Traditional lead capture solutions require a backend server, database, and API endpoints. LeadGen.js eliminates this complexity by using Google Sheets as a no-code backend. Simply include the script, configure your Google Sheets web app URL, and your forms will automatically submit data to your spreadsheet.

Key Features

Use Cases

Getting Started

This guide walks you through setting up LeadGen.js from scratch. You will need a Google account and a basic HTML page.

Prerequisites

Step 1: Create Your HTML Form

Create an HTML file with a form. The form should have input fields with name attributes - these names will become column headers in your Google Sheet.

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>My Lead Form</title>
</head>
<body>
    <form id="my-lead-form">
        <input type="text" name="firstName" placeholder="First Name" required>
        <input type="text" name="lastName" placeholder="Last Name" required>
        <input type="email" name="email" placeholder="Email Address" required>
        <input type="tel" name="phone" placeholder="Phone Number">
        <select name="interest">
            <option value="product">Product Inquiry</option>
            <option value="support">Support</option>
            <option value="other">Other</option>
        </select>
        <textarea name="message" placeholder="Your message"></textarea>
        <button type="submit">Submit</button>
    </form>
    <script src="https://cdn.jsdelivr.net/gh/NileGazer00/leadgen.js/leadgen.js"></script>
    <script>
        LeadGen.init({
            formId: "my-lead-form",
            sheetUrl: "YOUR_GOOGLE_APPS_SCRIPT_WEB_APP_URL",
            theme: "light"
        });
    </script>
</body>
</html>
Note: The name attributes on your form inputs are critical. They determine the column headers in your Google Sheet. Use camelCase or snake_case for consistency.

Step 2: Set Up Google Sheets

Follow the Google Sheets Setup Guide to create your Google Apps Script web app URL.

Step 3: Initialize LeadGen.js

Replace YOUR_GOOGLE_APPS_SCRIPT_WEB_APP_URL with the URL you received from publishing your Google Apps Script. When a user submits the form, the data will be sent to your Google Sheet automatically.

Step 4: Test Your Form

Open your HTML page in a browser, fill out the form, and submit it. Check your Google Sheet - you should see a new row with the submitted data. Open the browser console (F12) to see debug logs from LeadGen.js.

Installation

LeadGen.js can be loaded in multiple ways depending on your project setup.

Option 1: CDN (Recommended for simple sites)

<script src="https://cdn.jsdelivr.net/gh/NileGazer00/leadgen.js/leadgen.js"></script>

Option 2: Download the file

<script src="/path/to/leadgen.js"></script>

Option 3: ES Module import

<script type="module">
    import { LeadGen } from "https://cdn.jsdelivr.net/gh/NileGazer00/leadgen.js/leadgen.module.js";
    LeadGen.init({ formId: "my-form", sheetUrl: "YOUR_WEB_APP_URL" });
</script>

Option 4: Using with npm/bundlers

npm install leadgen-js
// CommonJS
const { LeadGen } = require("leadgen-js");
// ES Modules
import { LeadGen } from "leadgen-js";
Tip: For production sites, use a specific version tag instead of @main to avoid breaking changes: https://cdn.jsdelivr.net/gh/NileGazer00/leadgen.js@1.0.0/leadgen.js

Google Sheets Setup Guide

This step-by-step guide explains how to set up Google Sheets to receive data from LeadGen.js using a Google Apps Script web app.

Step 1: Create a New Google Sheet

  1. Go to sheets.google.com
  2. Click "+ Blank" to create a new spreadsheet
  3. Name your spreadsheet (e.g., "Lead Capture Data")
  4. In row 1, add your column headers exactly matching your form field names: | firstName | lastName | email | phone | interest | message | timestamp |
Important: The column headers must exactly match the name attributes on your HTML form inputs. LeadGen.js automatically adds a timestamp column.

Step 2: Open Google Apps Script

  1. In your Google Sheet, click Extensions - Apps Script
  2. A new tab will open with the Apps Script editor
  3. Delete any existing code in the editor

Step 3: Paste the Server-Side Code

function doPost(e) {
  try {
    var sheet = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();
    var data = JSON.parse(e.postData.contents);
    data.timestamp = new Date().toISOString();
    var headers = sheet.getRange(1, 1, 1, sheet.getLastColumn()).getValues()[0];
    if (headers.indexOf("timestamp") === -1) {
      sheet.appendRow(["timestamp"]);
      headers = sheet.getRange(1, 1, 1, sheet.getLastColumn()).getValues()[0];
    }
    var rowData = headers.map(function(header) {
      return data[header] || "";
    });
    sheet.appendRow(rowData);
    return ContentService
      .createTextOutput(JSON.stringify({ status: "success", message: "Lead captured successfully" }))
      .setMimeType(ContentService.MimeType.JSON);
  } catch (error) {
    return ContentService
      .createTextOutput(JSON.stringify({ status: "error", message: error.toString() }))
      .setMimeType(ContentService.MimeType.JSON);
  }
}

function doGet(e) {
  return ContentService
    .createTextOutput(JSON.stringify({ status: "active", message: "LeadGen.js web app is running" }))
    .setMimeType(ContentService.MimeType.JSON);
}

Step 4: Deploy as Web App

  1. Click Deploy - New deployment
  2. Click the gear icon next to "Select type" and choose Web app
  3. Set the following options:
    • Description: "LeadGen.js API"
    • Execute as: "Me" (your Google account)
    • Who has access: "Anyone"
  4. Click Deploy
  5. Authorize the script when prompted (click your account - Advanced - Go to script)
  6. Copy the Web app URL - this is your sheetUrl
Warning: You MUST set "Who has access" to "Anyone" or your form submissions will fail with a 401 error. The script runs under your Google account permissions, so your sheet remains private.

Step 5: Test the Web App

Open the web app URL in your browser. You should see:

{"status":"active","message":"LeadGen.js web app is running"}

If you see this, your web app is working correctly.

API Reference

LeadGen.init(config)

Initializes LeadGen.js on a form element. This is the main entry point for the library. Call this method after the DOM is loaded and after including the LeadGen.js script.

Parameters

ParameterTypeRequiredDefaultDescription
configObjectYes-Configuration object
config.formIdStringYesnullThe ID of the HTML form element to attach LeadGen to
config.sheetUrlStringYesnullThe Google Apps Script web app URL
config.themeStringNo"light"Theme for form styling: "light", "dark", or "none"
config.validateBooleanNotrueEnable built-in form validation
config.debugBooleanNofalseEnable console debug logging
config.onSuccessFunctionNonullCallback function fired after successful submission
config.onErrorFunctionNonullCallback function fired when submission fails
config.customHeadersObjectNo{}Additional HTTP headers sent with each request

Returns

void

Example

LeadGen.init({
    formId: "contact-form",
    sheetUrl: "https://script.google.com/macros/s/YOUR_ID/exec",
    theme: "dark",
    validate: true,
    debug: true,
    onSuccess: function(response) {
        console.log("Lead captured:", response);
        alert("Thank you! We will be in touch.");
    },
    onError: function(error) {
        console.error("Submission failed:", error);
        alert("Something went wrong. Please try again.");
    }
});

LeadGen.calculateNeeded(target, avg)

Calculates the number of leads needed to reach a revenue target based on an average customer value. This is useful for marketing planning and setting lead generation goals.

Parameters

ParameterTypeRequiredDescription
targetNumberYesThe revenue target in dollars (e.g., 21000)
avgNumberYesThe average revenue per customer in dollars (e.g., 499)

Returns

Number - The number of leads needed (rounded up to the nearest integer)

Example

var leads = LeadGen.calculateNeeded(21000, 499);
console.log(leads); // Output: 43

var subs = LeadGen.calculateNeeded(5000, 99);
console.log(subs); // Output: 51
Formula: Math.ceil(target / avg) - The result is always rounded up because partial leads do not exist.

LeadGen.setTheme(theme)

Changes the form theme at runtime without re-initializing. Useful for implementing dark mode toggles.

Parameters

ParameterTypeRequiredDescription
themeStringYes"light", "dark", or "none" (removes all LeadGen styling)

Returns

void

Example

document.getElementById("dark-toggle").addEventListener("click", function() {
    LeadGen.setTheme("dark");
});
document.getElementById("light-toggle").addEventListener("click", function() {
    LeadGen.setTheme("light");
});

LeadGen.getAnalytics()

Returns analytics data about form submissions stored in the current session. Data is stored in sessionStorage and resets when the browser session ends.

Returns

Object - An analytics object with the following properties:

PropertyTypeDescription
totalAttemptsNumberTotal number of form submission attempts
successfulNumberNumber of successful submissions
failedNumberNumber of failed submissions
validationErrorsNumberNumber of submissions blocked by validation
conversionRateStringPercentage of successful submissions (e.g., "85.7%")

Example

var stats = LeadGen.getAnalytics();
console.log("Conversion rate:", stats.conversionRate);
console.log("Successful:", stats.successful);
console.log("Failed:", stats.failed);

LeadGen.destroy()

Removes all LeadGen event listeners and resets the instance. Useful for single-page applications where forms are dynamically added and removed from the DOM.

Returns

void

Example

LeadGen.destroy();
document.getElementById("my-form").remove();
LeadGen.init({ formId: "my-form", sheetUrl: url });

LeadGen.validateForm(formId)

Manually triggers form validation without submitting. Returns validation results so you can display custom error messages.

Returns

Object - { valid: Boolean, errors: String[] }

Example

var result = LeadGen.validateForm("my-form");
if (!result.valid) {
    console.log("Invalid fields:", result.errors);
    result.errors.forEach(function(err) {
        console.warn(err);
    });
}

Examples

Example 1: Basic Contact Form

<form id="basic-form">
    <input type="text" name="name" placeholder="Your Name" required>
    <input type="email" name="email" placeholder="Email" required>
    <textarea name="message" placeholder="Message"></textarea>
    <button type="submit">Send</button>
</form>
<script src="https://cdn.jsdelivr.net/gh/NileGazer00/leadgen.js/leadgen.js"></script>
<script>
    LeadGen.init({
        formId: "basic-form",
        sheetUrl: "https://script.google.com/macros/s/YOUR_ID/exec"
    });
</script>

Example 2: Form with Custom Validation and Callbacks

LeadGen.init({
    formId: "signup-form",
    sheetUrl: "https://script.google.com/macros/s/YOUR_ID/exec",
    validate: true,
    debug: true,
    onSuccess: function(response) {
        document.getElementById("signup-form").style.display = "none";
        document.getElementById("success-msg").style.display = "block";
    },
    onError: function(error) {
        document.getElementById("error-msg").textContent = "Error: " + error.message;
        document.getElementById("error-msg").style.display = "block";
    }
});

Example 3: Using with React

import React, { useEffect, useRef } from "react";

function LeadForm() {
    const formRef = useRef(null);
    useEffect(function() {
        const script = document.createElement("script");
        script.src = "https://cdn.jsdelivr.net/gh/NileGazer00/leadgen.js/leadgen.js";
        script.onload = function() {
            LeadGen.init({
                formId: "react-lead-form",
                sheetUrl: "https://script.google.com/macros/s/YOUR_ID/exec",
                validate: true,
                onSuccess: function() { alert("Lead captured!"); }
            });
        };
        document.body.appendChild(script);
        return function() {
            LeadGen.destroy();
            document.body.removeChild(script);
        };
    }, []);
    return (
        <form id="react-lead-form" ref={formRef}>
            <input type="text" name="name" placeholder="Name" required />
            <input type="email" name="email" placeholder="Email" required />
            <button type="submit">Submit</button>
        </form>
    );
}

Example 4: Using with Vue 3

<template>
    <form id="vue-lead-form">
        <input type="text" name="name" v-model="form.name" placeholder="Name">
        <input type="email" name="email" v-model="form.email" placeholder="Email">
        <button type="submit">Submit</button>
    </form>
</template>
<script>
export default {
    data() { return { form: { name: "", email: "" } }; },
    mounted() {
        const script = document.createElement("script");
        script.src = "https://cdn.jsdelivr.net/gh/NileGazer00/leadgen.js/leadgen.js";
        script.onload = () => {
            LeadGen.init({
                formId: "vue-lead-form",
                sheetUrl: "https://script.google.com/macros/s/YOUR_ID/exec"
            });
        };
        document.head.appendChild(script);
    },
    beforeUnmount() { LeadGen.destroy(); }
};
</script>

Example 5: Multi-Step Form

<div id="step-1" class="form-step">
    <input type="text" name="firstName" placeholder="First Name" required>
    <input type="text" name="lastName" placeholder="Last Name" required>
    <button type="button" onclick="nextStep()">Next</button>
</div>
<div id="step-2" class="form-step" style="display:none;">
    <input type="email" name="email" placeholder="Email" required>
    <input type="tel" name="phone" placeholder="Phone">
    <button type="button" onclick="prevStep()">Back</button>
    <button type="button" onclick="submitForm()">Submit</button>
</div>
<script>
function nextStep() {
    document.getElementById("step-1").style.display = "none";
    document.getElementById("step-2").style.display = "block";
}
function prevStep() {
    document.getElementById("step-2").style.display = "none";
    document.getElementById("step-1").style.display = "block";
}
function submitForm() {
    var data = {
        firstName: document.querySelector('[name="firstName"]').value,
        lastName: document.querySelector('[name="lastName"]').value,
        email: document.querySelector('[name="email"]').value,
        phone: document.querySelector('[name="phone"]').value
    };
    LeadGen.sendData(data);
}
</script>

Example 6: Dark Mode Toggle

<button id="toggle-theme">Toggle Dark Mode</button>
<script>
document.getElementById("toggle-theme").addEventListener("click", function() {
    var current = document.body.getAttribute("data-theme");
    if (current === "dark") {
        document.body.setAttribute("data-theme", "light");
        LeadGen.setTheme("light");
    } else {
        document.body.setAttribute("data-theme", "dark");
        LeadGen.setTheme("dark");
    }
});
</script>

Configuration Options

Full reference of all configuration options available in LeadGen.init().

OptionTypeDefaultDescription
formIdStringnullID of the form element to attach to
sheetUrlStringnullGoogle Apps Script web app URL
themeString"light""light", "dark", or "none"
validateBooleantrueEnable HTML5 form validation
debugBooleanfalseLog all operations to console
onSuccessFunctionnullSuccess callback: function(response)
onErrorFunctionnullError callback: function(error)
customHeadersObject{}Extra HTTP headers for requests
timeoutNumber10000Request timeout in milliseconds
redirectUrlStringnullURL to redirect to after successful submission
resetFormBooleantrueReset form fields after successful submission
disableOnSubmitBooleantrueDisable submit button while request is in progress

Troubleshooting

"Form submission failed" error

Cause: The Google Apps Script web app URL is incorrect or the script has an error.

Fix:

  1. Open your web app URL in a browser - you should see {"status":"active"}
  2. If you see an error, re-deploy the script in Google Apps Script
  3. Make sure "Who has access" is set to "Anyone"
  4. Check the URL does not have trailing spaces or characters

CORS error in browser console

Cause: Google Apps Script blocks cross-origin requests when not properly deployed.

Fix:

  1. Go to Google Apps Script - Deploy - Manage deployments
  2. Click the pencil icon to edit the deployment
  3. Set "Execute as" to "Me"
  4. Set "Who has access" to "Anyone"
  5. Save and test the URL again in your browser

Data not appearing in Google Sheet

Cause: Column headers in the sheet do not match form field names.

Fix:

  1. Compare your form input name attributes with your Google Sheet column headers
  2. They must match exactly (case-sensitive)
  3. Enable debug mode: LeadGen.init({ ..., debug: true })
  4. Check the console output to see what data is being sent

Form submits but page reloads

Cause: LeadGen.js is not intercepting the form submit event.

Fix:

  1. Make sure the formId matches the actual ID of your form element
  2. Make sure LeadGen.init() is called after the form exists in the DOM
  3. Wrap your init call in DOMContentLoaded or place the script at the bottom of the body
document.addEventListener("DOMContentLoaded", function() {
    LeadGen.init({ formId: "my-form", sheetUrl: "YOUR_URL" });
});

"401 Unauthorized" from Google Apps Script

Cause: The web app is not set to allow anonymous access.

Fix: Re-deploy with "Who has access" set to "Anyone" - see Step 4 in the Google Sheets Setup Guide.

Validation errors on valid inputs

Cause: HTML5 validation attributes (required, pattern, type) are too strict.

Fix: Either adjust your HTML attributes or disable built-in validation:

LeadGen.init({ formId: "my-form", sheetUrl: "YOUR_URL", validate: false });

Frequently Asked Questions

Is LeadGen.js free?

Yes. LeadGen.js is released under the MIT License, which means it is free for personal and commercial use. You can use it in any project without paying fees or royalties.

Does it work with React/Vue/Angular?

Yes. LeadGen.js is framework-agnostic because it operates on standard HTML form elements. See the Examples section for React and Vue integration code.

Is my Google Sheet data secure?

Your Google Sheet remains private. The Google Apps Script runs under your Google account permissions. External users can only send data to your sheet through the web app endpoint - they cannot read or modify existing data.

Can I use this on GitHub Pages?

Yes. LeadGen.js is designed specifically for static hosting platforms like GitHub Pages, Netlify, Vercel, and Cloudflare Pages. No server-side code is needed.

What happens if Google Apps Script is down?

Google Apps Script has a 99.9% uptime SLA. If it does go down, form submissions will fail and the onError callback will fire. You can implement a fallback such as showing a message asking users to email you directly.

Is there a rate limit?

Google Apps Script has a quota of 20,000 URL fetch requests per day for free accounts. For most lead generation forms, this is more than sufficient. Google Workspace accounts have higher limits.

Can I submit data to multiple sheets?

Yes. Initialize multiple instances with different form IDs and sheet URLs:

LeadGen.init({
    formId: "contact-form",
    sheetUrl: "https://script.google.com/macros/s/ID_CONTACT/exec"
});
LeadGen.init({
    formId: "newsletter-form",
    sheetUrl: "https://script.google.com/macros/s/ID_NEWSLETTER/exec"
});

Can I customize the form styling?

Yes. Set theme: "none" to disable all LeadGen styling and apply your own CSS. The library only modifies the form element you specify and does not affect the rest of your page.

Does it support file uploads?

No. LeadGen.js is designed for text-based form data. File uploads require a backend server for processing and storage.

How do I add a honeypot field for spam protection?

<input type="text" name="website" style="display:none;" tabindex="-1" autocomplete="off">
<script>
LeadGen.init({
    formId: "my-form",
    sheetUrl: "YOUR_URL",
    onSuccess: function(response) {
        var honeypot = document.querySelector('[name="website"]').value;
        if (honeypot) {
            console.warn("Spam detected - honeypot field was filled");
            return;
        }
    }
});
</script>

Contributing

Contributions to LeadGen.js are welcome. This is an open-source project and we appreciate all help from the JavaScript community.

How to Contribute

  1. Fork the repository on GitHub
  2. Create a feature branch: git checkout -b feature/your-feature-name
  3. Make your changes with clear, commented code
  4. Test your changes in multiple browsers (Chrome, Firefox, Safari, Edge)
  5. Commit with a descriptive message: git commit -m "Add: description of change"
  6. Push to your fork: git push origin feature/your-feature-name
  7. Open a Pull Request against the main branch

Reporting Bugs

If you find a bug, please open a GitHub issue with:

Code Style

License

By contributing to LeadGen.js, you agree that your contributions will be licensed under the MIT License.