Integration

Forms Integration

Integrate TownHall forms using HTML, JavaScript, or React. Choose the method that fits your stack.

HTML Forms

The simplest way to collect form submissions - no JavaScript required.

Basic Form

HTML
<form action="https://townhall.gg/f/YOUR_FORM_ID" method="POST">
  <label>
    Name
    <input type="text" name="name" required>
  </label>
  
  <label>
    Email
    <input type="email" name="email" required>
  </label>
  
  <label>
    Message
    <textarea name="message" required></textarea>
  </label>
  
  <button type="submit">Send Message</button>
</form>

With Custom Fields

Use any field names you want - TownHall accepts them all:

HTML
<form action="https://townhall.gg/f/YOUR_FORM_ID" method="POST">
  <input type="text" name="company_name" required>
  <input type="email" name="work_email" required>
  <input type="tel" name="phone_number">
  <select name="inquiry_type">
    <option value="sales">Sales</option>
    <option value="support">Support</option>
  </select>
  <textarea name="details" required></textarea>
  <button type="submit">Send</button>
</form>
All fields captured

All fields are captured and stored. Auto-reply will find the email automatically!

Redirect After Submit

Configure a redirect URL in your form settings to send users to a thank-you page after submission.

JavaScript / AJAX

Submit forms without page reload using fetch or XMLHttpRequest.

Using Fetch API

JavaScript
// Submit form data as JSON
const submitForm = async (formData) => {
  const response = await fetch('https://townhall.gg/f/YOUR_FORM_ID', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({
      name: formData.name,
      email: formData.email,
      message: formData.message
    })
  });
  
  const result = await response.json();
  
  if (response.ok) {
    console.log('Success:', result.message);
    console.log('Submission ID:', result.id);
  } else {
    console.error('Error:', result.error);
  }
};

Response Format

Successful submissions return:

JSON
{
  "success": true,
  "message": "Thank you for your submission!",
  "id": "550e8400-e29b-41d4-a716-446655440000"
}

Error responses return:

JSON
{
  "error": "Form not found"
}

// Or
{
  "error": "Form is not accepting submissions"
}

Using FormData

JavaScript
document.querySelector('form').addEventListener('submit', async (e) => {
  e.preventDefault();
  
  const formData = new FormData(e.target);
  
  // Convert to JSON
  const data = Object.fromEntries(formData.entries());
  
  const response = await fetch('https://townhall.gg/f/YOUR_FORM_ID', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify(data)
  });
  
  if (response.ok) {
    alert('Form submitted successfully!');
    e.target.reset();
  }
});

React / Next.js

Build beautiful forms in React with full control over the submission flow.

React Component Example

React
import { useState } from 'react';

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

  const handleSubmit = async (e) => {
    e.preventDefault();
    setStatus('loading');
    
    const formData = new FormData(e.target);
    const data = Object.fromEntries(formData.entries());
    
    try {
      const response = await fetch('https://townhall.gg/f/YOUR_FORM_ID', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify(data)
      });
      
      const result = await response.json();
      
      if (response.ok) {
        setStatus('success');
        setMessage(result.message);
        e.target.reset();
      } else {
        setStatus('error');
        setMessage(result.error);
      }
    } catch (err) {
      setStatus('error');
      setMessage('Failed to submit form');
    }
  };

  return (
    <form onSubmit={handleSubmit}>
      <input type="text" name="name" placeholder="Your name" required />
      <input type="email" name="email" placeholder="Your email" required />
      <textarea name="message" placeholder="Your message" required />
      
      <button type="submit" disabled={status === 'loading'}>
        {status === 'loading' ? 'Sending...' : 'Send Message'}
      </button>
      
      {status === 'success' && <p className="success">{message}</p>}
      {status === 'error' && <p className="error">{message}</p>}
    </form>
  );
}

Custom Hook

useTownhall.js
import { useState, useCallback } from 'react';

export function useTownhall(formId) {
  const [status, setStatus] = useState('idle');
  const [error, setError] = useState(null);
  
  const submit = useCallback(async (data) => {
    setStatus('loading');
    setError(null);
    
    try {
      const res = await fetch(`https://townhall.gg/f/${formId}`, {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify(data)
      });
      
      const result = await res.json();
      
      if (!res.ok) throw new Error(result.error);
      
      setStatus('success');
      return result;
    } catch (err) {
      setStatus('error');
      setError(err.message);
      throw err;
    }
  }, [formId]);
  
  return { submit, status, error, isLoading: status === 'loading' };
}