HTML/JavaScript Integration

Direct integration for custom websites with full control over implementation

Overview

Add the SpamBlock pixel to any static site or custom JavaScript application. The script protects native HTML forms by default and exposes hooks if you need more control.

Prerequisites

  • Ability to edit the HTML template or bundle a JavaScript asset
  • Forms that render as native <form> elements in the DOM
  • Optional: build tooling if you prefer to load the script from a module bundler

Setup

Quick start

<script src="https://api.spamblock.io/sdk/pixel/v1.js" defer></script>

<form data-block-spam action="/api/contact" method="post">
  <input type="email" name="email" required />
  <textarea name="message" required></textarea>
  <button type="submit">Send</button>
</form>

Add data-block-spam to any form you want protected. If you omit the attribute, all forms on the page are protected automatically.

Configuration

All configuration options are set via data-* attributes on the script tag. See the complete configuration reference for all available options and their defaults.

Advanced usage

Manual protection

window.addEventListener('DOMContentLoaded', () => {
  const form = document.getElementById('contact-form');
  if (form) {
    form.setAttribute('data-block-spam', 'true');
  }
});

React example

import { useEffect, useRef } from 'react';

export function ContactForm() {
  const formRef = useRef(null);

  useEffect(() => {
    if (formRef.current) {
      formRef.current.setAttribute('data-block-spam', 'true');
    }
  }, []);

  return (
    <form ref={formRef} action="/api/contact" method="post">
      <input type="email" name="email" required />
      <textarea name="message" required></textarea>
      <button type="submit">Send</button>
    </form>
  );
}

Form Submission Methods

SpamBlock supports two submission patterns: native form submission (default) and JavaScript-based submission (for SPAs and custom handling).

Native Form Submission (Default)

By default, SpamBlock uses native form submission via form.requestSubmit(). This preserves browser validation, form actions, and submitter buttons. The form will navigate to the URL specified in the action attribute.

<form data-block-spam action="/api/contact" method="post">
  <input type="email" name="email" required />
  <textarea name="message" required></textarea>
  <button type="submit">Send</button>
</form>

When to use:

  • Traditional server-rendered forms
  • Forms that submit to server endpoints
  • When you want browser-native validation and navigation
  • Simple, zero-configuration setups

How it works:

  1. SpamBlock intercepts the submit event
  2. Checks the submission with the API
  3. If allowed: calls form.requestSubmit() to trigger native submission
  4. Browser navigates to the form's action URL

JavaScript-Based Submission

For single-page applications (SPAs) or when you need custom submission handling, you need to prevent the pixel's default native submission and handle it manually. Here's how:

Important: The pixel calls form.requestSubmit() immediately after emitting the spamblock:allowed event. To prevent native submission, you have two options:

Option 1: Remove the action attribute (Recommended)

<form data-block-spam id="contact-form">
  <!-- No action attribute - form won't navigate -->
  <input type="email" name="email" required />
  <textarea name="message" required></textarea>
  <button type="submit">Send</button>
  <div id="status-message"></div>
</form>

<script>
document.addEventListener('DOMContentLoaded', () => {
  const form = document.getElementById('contact-form');
  
  // Intercept allowed event to handle submission manually
  form.addEventListener('spamblock:allowed', async (event) => {
    // Get form data
    const formData = new FormData(form);
    
    // Submit via fetch (or your preferred method)
    try {
      const response = await fetch('/api/contact', {
        method: 'POST',
        body: formData
      });
      
      if (response.ok) {
        document.getElementById('status-message').textContent = '✅ Message sent!';
        form.reset();
      } else {
        throw new Error('Submission failed');
      }
    } catch (error) {
      document.getElementById('status-message').textContent = '❌ Error sending message';
    }
  });
  
  // Handle blocked submissions
  form.addEventListener('spamblock:blocked', (event) => {
    const response = event.detail.response;
    document.getElementById('status-message').textContent = 
      `❌ Submission blocked: ${response?.reasons?.join(', ') || 'Spam detected'}`;
  });
});
</script>

Option 2: Intercept the original submit event

<form data-block-spam id="contact-form" action="/api/contact" method="post">
  <input type="email" name="email" required />
  <textarea name="message" required></textarea>
  <button type="submit">Send</button>
  <div id="status-message"></div>
</form>

<script>
document.addEventListener('DOMContentLoaded', () => {
  const form = document.getElementById('contact-form');
  let shouldSubmitNatively = false;
  
  // Intercept submit BEFORE SpamBlock to prevent native submission
  form.addEventListener('submit', (event) => {
    // Only allow native submission if we explicitly set the flag
    if (!shouldSubmitNatively) {
      event.preventDefault();
    }
  }, { capture: true }); // Capture phase - runs before SpamBlock's handler
  
  // Handle SpamBlock allowed event
  form.addEventListener('spamblock:allowed', async (event) => {
    // Prevent the pixel's requestSubmit() from actually submitting
    // by ensuring preventDefault was already called
    const formData = new FormData(form);
    
    try {
      const response = await fetch('/api/contact', {
        method: 'POST',
        body: formData
      });
      
      if (response.ok) {
        document.getElementById('status-message').textContent = '✅ Message sent!';
        form.reset();
      }
    } catch (error) {
      document.getElementById('status-message').textContent = '❌ Error sending message';
    }
  });
  
  form.addEventListener('spamblock:blocked', (event) => {
    const response = event.detail.response;
    document.getElementById('status-message').textContent = 
      `❌ Submission blocked: ${response?.reasons?.join(', ') || 'Spam detected'}`;
  });
});
</script>

When to use:

  • Single-page applications (React, Vue, Angular)
  • Forms that need to stay on the same page
  • Custom submission logic (API calls, analytics, etc.)
  • When you need full control over the submission flow

Key differences:

  • Native: Form navigates to action URL, browser handles everything
  • JavaScript: Form stays on page, you handle submission and UI

Listening for results

The pixel dispatches custom DOM events you can hook into:

// Submission was allowed - form will submit natively by default
form.addEventListener('spamblock:allowed', (event) => {
  const response = event.detail.response;
  console.log('Submission allowed', response);
  // response contains: { allow: true, score: 0-60, reasons: [], latencyMs: 123 }
});

// Submission was blocked - form will NOT submit
form.addEventListener('spamblock:blocked', (event) => {
  const response = event.detail.response;
  console.warn('Spam detected', response);
  // response contains: { allow: false, score: 61+, reasons: ['disposable_domain'], latencyMs: 123 }
});

Event details:

  • event.detail.response - The SpamBlock API response
  • event.detail.latencyMs - Time taken for the check (milliseconds)
  • event.detail.site - The site domain that was checked

Preventing default submission: The pixel automatically calls form.requestSubmit() after emitting spamblock:allowed. To prevent this:

  • Option 1 (Recommended): Remove the action attribute from your form. Without an action, requestSubmit() won't navigate anywhere.
  • Option 2: Intercept the original submit event in the capture phase (before SpamBlock) and call preventDefault() to stop all submission attempts.

Testing

  • Enable debug logging by setting data-debug="true" on the script tag.
  • Submit a disposable address like [email protected] to trigger the disposable domain signal.
  • Try spam-heavy text ("casino winner viagra") to trigger profanity detection.
  • Use browser devtools to ensure the honeypot field _sb_hp is present and left blank.

For more testing tips and configuration options, see the documentation.

Troubleshooting

Symptom Fix
Form never submits Check for competing JavaScript that also prevents submission.
Legitimate submissions blocked Adjust data-max-score or inspect console logs (with data-debug="true") for the reasons array. See configuration options.
No spam being caught Confirm the pixel loads on the page and the <form> has data-block-spam.

For more troubleshooting tips and configuration options, see the documentation.