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' };
}