SDK
JavaScript SDK
Official TypeScript SDK for TownHall form submissions. Full type safety with React hooks and vanilla JavaScript support.
1
Installation
Choose the package that fits your stack:
React / Next.js
bash
npm install @townhall-gg/reactVanilla JavaScript / TypeScript
bash
npm install @townhall-gg/corePackage Sizes
@townhall-gg/core is ~3.6KB and @townhall-gg/react is ~1.9KB (minified). Both are tree-shakeable with zero external dependencies.
2
React Usage
Use the useTownHallForm hook for the best developer experience with automatic state management.
Basic Contact Form
tsx
import { useTownHallForm } from '@townhall-gg/react'
function ContactForm() {
const { submit, isSubmitting, isSuccess, error } = useTownHallForm('YOUR_FORM_ID')
const handleSubmit = async (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault()
const formData = new FormData(e.currentTarget)
await submit(Object.fromEntries(formData))
}
if (isSuccess) {
return (
<div className="success">
<h2>Thank you!</h2>
<p>Your message has been sent.</p>
</div>
)
}
return (
<form onSubmit={handleSubmit}>
<input name="name" placeholder="Your name" required />
<input name="email" type="email" placeholder="Email" required />
<textarea name="message" placeholder="Message" required />
<button type="submit" disabled={isSubmitting}>
{isSubmitting ? 'Sending...' : 'Send Message'}
</button>
{error && <p className="error">{error.message}</p>}
</form>
)
}With Reset Functionality
Allow users to submit multiple responses:
tsx
function NewsletterForm() {
const { submit, isSubmitting, isSuccess, error, reset } = useTownHallForm('FORM_ID')
if (isSuccess) {
return (
<div>
<p>You're subscribed!</p>
<button onClick={reset}>Subscribe another email</button>
</div>
)
}
const handleSubmit = async (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault()
const formData = new FormData(e.currentTarget)
await submit(Object.fromEntries(formData))
}
return (
<form onSubmit={handleSubmit}>
<input name="email" type="email" placeholder="Enter your email" required />
<button disabled={isSubmitting}>Subscribe</button>
{error && <span>{error.message}</span>}
</form>
)
}3
Vanilla JavaScript Usage
Use @townhall-gg/core directly for non-React projects or server-side code.
Creating a Client
ts
import { createClient } from '@townhall-gg/core'
const client = createClient('YOUR_FORM_ID')
// Submit with result pattern (recommended)
const result = await client.submit({
name: 'John Doe',
email: 'john@example.com',
message: 'Hello from TownHall!'
})
if (result.success) {
console.log('Submission ID:', result.data.id)
console.log('Message:', result.data.message)
} else {
console.error('Error:', result.error.message)
}One-off Submission
ts
import { submit } from '@townhall-gg/core'
// Quick one-liner for simple use cases
const result = await submit('YOUR_FORM_ID', {
email: 'user@example.com',
message: 'Quick submission'
})Try/Catch Pattern
ts
import { createClient, TownHallError } from '@townhall-gg/core'
const client = createClient('YOUR_FORM_ID')
try {
const response = await client.submitOrThrow({
email: 'user@example.com'
})
console.log('Success!', response.id)
} catch (error) {
if (error instanceof TownHallError) {
if (error.isRateLimited) {
console.log('Too many requests, please wait')
} else if (error.isNotFound) {
console.log('Form not found')
} else {
console.log('Error:', error.message)
}
}
}Error Handling
The SDK provides typed errors with helpful properties to handle different failure scenarios:
| Property | Type | Description |
|---|---|---|
error.status | number | HTTP status code |
error.code | string | Error code (e.g., "RATE_LIMITED", "NOT_FOUND") |
error.isRateLimited | boolean | True if rate limited (429) |
error.isNotFound | boolean | True if form not found (404) |
error.isFormInactive | boolean | True if form is disabled |
error.isValidationError | boolean | True if validation failed (400) |
tsx
// React error handling
const { error } = useTownHallForm('form-id')
if (error) {
if (error.isRateLimited) {
return <p>Too many submissions. Please wait a moment.</p>
}
if (error.isFormInactive) {
return <p>This form is no longer accepting submissions.</p>
}
return <p>Error: {error.message}</p>
}TypeScript Support
Full TypeScript support with exported types:
ts
import type {
TownHallResponse,
TownHallError,
TownHallConfig,
FormData,
SubmitResult,
} from '@townhall-gg/core'
// Response type
interface TownHallResponse {
success: true
message: string // Success message (customizable in dashboard)
id: string // Submission ID
emails: {
notifications: { enabled: boolean; count: number }
autoReply: { enabled: boolean; willSend: boolean }
}
warning?: string // Plan limit warning if applicable
}Configuration
Customize the client behavior:
ts
// Vanilla JS
const client = createClient('YOUR_FORM_ID', {
baseUrl: 'https://your-custom-domain.com', // Self-hosted TownHall
timeout: 10000, // 10 second timeout (default: 30000)
})
// React - using provider for global config
import { TownHallProvider } from '@townhall-gg/react'
function App() {
return (
<TownHallProvider
baseUrl="https://your-domain.com"
timeout={15000}
>
<YourApp />
</TownHallProvider>
)
}API Reference
useTownHallForm(formId, config?)
React hook for form submissions.
| Return Value | Type | Description |
|---|---|---|
isSubmitting | boolean | True while submission is in progress |
isSuccess | boolean | True after successful submission |
error | TownHallError | null | Error from last submission |
data | TownHallResponse | null | Response data on success |
submit | (data) => Promise | Submit form data |
reset | () => void | Reset form state |
createClient(formId, config?)
Create a TownHall client instance.
| Parameter | Type | Description |
|---|---|---|
formId | string | Your TownHall form ID |
config.baseUrl | string | API base URL (default: https://townhall.gg) |
config.timeout | number | Request timeout in ms (default: 30000) |