How to add a contact form to a static site — no backend required
AI-generated sites look great. The one thing they can't do out of the box is handle form submissions — there's no server to receive them. This guide shows how to wire up a working contact form in under 3 minutes using JS2Mail.
Works with any AI builder or static host
JS2Mail doesn't touch your HTML or CSS — it only receives the POST. Whatever tool generated your site, just swap the form's action URL.
Set up in 3 steps
-
Create a free account and connect your mailbox Sign up at js2mail.dev. Connect Gmail, Outlook, or any SMTP account. Submissions will arrive in that inbox.
-
Create a Form Endpoint Go to Dashboard → Forms → Create. Name it, set the destination email, optionally set a redirect URL for after submission. Copy the
publicId. -
Paste the action URL into your HTML Replace
YOUR_FORM_IDbelow with yourpublicId. No JavaScript, no server, no build step.
Code examples
<form action="https://js2mail.dev/f/YOUR_FORM_ID" method="POST">
<input name="name" placeholder="Your name" required>
<input name="email" type="email" placeholder="Email" required>
<textarea name="message" placeholder="Message" required></textarea>
<!-- Honeypot: JS2Mail silently drops filled submissions -->
<input name="_honey" style="display:none" tabindex="-1" autocomplete="off">
<!-- Optional: redirect after submit -->
<input type="hidden" name="redirect_url" value="https://yoursite.com/thanks">
<button type="submit">Send message</button>
</form>
Submitting redirects to redirect_url. Omit it and JS2Mail returns {"ok":true} JSON instead.
<form id="contact">
<input name="name" required>
<input name="email" type="email" required>
<textarea name="message" required></textarea>
<input name="_honey" style="display:none" tabindex="-1">
<button type="submit">Send</button>
</form>
<script>
document.getElementById('contact').addEventListener('submit', async e => {
e.preventDefault();
const res = await fetch('https://js2mail.dev/f/YOUR_FORM_ID', {
method: 'POST',
headers: { 'Content-Type': 'application/x-www-form-urlencoded', 'Accept': 'application/json' },
body: new URLSearchParams(new FormData(e.target)).toString()
});
if (res.ok) alert('Message sent!');
});
</script>
User stays on the page. JS2Mail returns {"ok":true} on success.
export function ContactForm() {
async function handleSubmit(e) {
e.preventDefault();
await fetch('https://js2mail.dev/f/YOUR_FORM_ID', {
method: 'POST',
headers: { 'Content-Type': 'application/x-www-form-urlencoded', 'Accept': 'application/json' },
body: new URLSearchParams(new FormData(e.target)).toString(),
});
}
return (
<form onSubmit={handleSubmit}>
<input name="name" required />
<input name="email" type="email" required />
<textarea name="message" required />
<input name="_honey" style={{ display: 'none' }} tabIndex={-1} />
<button type="submit">Send message</button>
</form>
);
}
Works in any React-based framework: Next.js, Remix, Vite, CRA.
Let your AI assistant do the wiring
If you're using Claude Desktop or Cursor, install the JS2Mail MCP server. Instead of telling the AI "go sign up at js2mail.dev", it provisions the account and hands you a working embed snippet automatically.
Claude Desktop
{
"mcpServers": {
"js2mail": {
"command": "npx",
"args": ["-y", "@js2mail/mcp"]
}
}
}
Add to ~/Library/Application Support/Claude/claude_desktop_config.json (macOS) or %APPDATA%\Claude\claude_desktop_config.json (Windows), restart Claude.
Cursor
{
"mcpServers": {
"js2mail": {
"command": "npx",
"args": ["-y", "@js2mail/mcp"]
}
}
}
Add to ~/.cursor/mcp.json. Then ask: "Add a contact form that sends to [email protected]" — the AI calls createFormEndpoint and drops in the HTML.
How it works under the hood
When a visitor submits your form, the browser POSTs to js2mail.dev/f/{publicId}.
JS2Mail validates the submission (honeypot, rate limiting, allowed origins), renders the fields into
an email, and sends it from your connected mailbox to your destination address.
The email arrives in your inbox like any normal message — with a subject, reply-to set to the
visitor's email, and full threading support.
Your HTML and CSS are untouched. JS2Mail never injects a widget, iframe, or script tag. You own the form's look, feel, and validation.
What gets sent to your inbox
Every field in the form becomes a line in the email body. No schema required — the template engine
picks up any name="…" attribute automatically.
You can also define a custom subject template in the Form Endpoint settings. For example:
New message from {{name}} — {{subject}}
The {{fieldName}} syntax resolves any posted field at send time.
Spam protection — built in
- Honeypot field — add
<input name="_honey" style="display:none">. Bots fill it; JS2Mail silently drops those submissions. - Rate limiting — 5 submissions per IP per minute by default (2/min in test mode). No config needed.
- Allowed origins — optionally restrict which domains can POST to your endpoint (CORS-style allowlist).
- Test mode — enable it while building locally; bypasses the origin check so
file://andlocalhostwork.
Frequently asked questions
How do I add a contact form to a static HTML site without a backend?
Set your <form action> to a JS2Mail endpoint URL — https://js2mail.dev/f/YOUR_FORM_ID. JS2Mail receives the POST and emails it to you. No server, no JavaScript, no infrastructure.
Does this work with GitHub Pages / Netlify / Vercel?
Yes — any static host works. The form POSTs directly to JS2Mail's servers. Your hosting platform only serves the HTML file.
Where do submissions land — a dashboard or my inbox?
Your inbox. Submissions arrive as normal emails from your own connected mailbox. You reply from your email client, threading works, and your CRM can process them like any inbound email.
Is JS2Mail free?
The Hobby plan is free forever — 10,000 submissions/month, unlimited forms, all features. No credit card required.
What's the difference between JS2Mail and Formspree or Web3Forms?
JS2Mail sends mail from your own connected mailbox (Gmail / Outlook / SMTP), so replies thread correctly in your inbox and the From address is yours — not a [email protected] address. See the full side-by-side comparison.
Can I use this with a Lovable, v0, or bolt.new site?
Yes. Just replace the form's action attribute with your JS2Mail endpoint URL. The AI-generated HTML and CSS remain unchanged.