For Svelte and SvelteKit, create a component that posts FormData to your FormSubmit endpoint with fetch and stores the status in a $state rune. It works with adapter-static, so you can host on GitHub Pages, Netlify, Vercel or Cloudflare Pages with no server.
Component (Svelte 5)
src/lib/ContactForm.svelte:
<script>
const ENDPOINT = "https://formsubmit.app/f/YOUR_FORM_ID";
let status = $state("idle");
let error = $state("");
const renderedAt = Date.now();
async function onsubmit(e) {
e.preventDefault();
status = "sending";
const data = new FormData(e.currentTarget);
data.set("_ts", String(renderedAt));
try {
const res = await fetch(ENDPOINT, { method: "POST", body: data, headers: { Accept: "application/json" } });
const json = await res.json();
if (!res.ok || !json.ok) throw new Error(json.error?.message ?? "Something went wrong");
status = "success";
} catch (err) {
error = err.message;
status = "error";
}
}
</script>
{#if status === "success"}
<p class="text-green-700">Thanks! We'll be in touch soon.</p>
{:else}
<form {onsubmit} class="mx-auto max-w-lg space-y-5">
<div class="space-y-1.5">
<label for="fs-name" class="block text-sm font-medium text-gray-900">Name</label>
<input id="fs-name" type="text" name="name" placeholder="Jane Doe" required class="block w-full rounded-lg border border-gray-300 px-3 py-2 text-sm shadow-sm focus:border-indigo-500 focus:outline-none focus:ring-2 focus:ring-indigo-500/20">
</div>
<div class="space-y-1.5">
<label for="fs-email" class="block text-sm font-medium text-gray-900">Email</label>
<input id="fs-email" type="email" name="email" placeholder="jane@example.com" required class="block w-full rounded-lg border border-gray-300 px-3 py-2 text-sm shadow-sm focus:border-indigo-500 focus:outline-none focus:ring-2 focus:ring-indigo-500/20">
</div>
<div class="space-y-1.5">
<label for="fs-message" class="block text-sm font-medium text-gray-900">Message</label>
<textarea id="fs-message" name="message" rows="5" placeholder="How can we help?" required class="block w-full rounded-lg border border-gray-300 px-3 py-2 text-sm shadow-sm focus:border-indigo-500 focus:outline-none focus:ring-2 focus:ring-indigo-500/20"></textarea>
</div>
<!-- Honeypot: leave this hidden field empty to catch bots -->
<input type="text" name="_gotcha" tabindex="-1" autocomplete="off" style="display:none">
{#if status === "error"}<p class="text-sm text-red-600">{error}</p>{/if}
<button type="submit" disabled={status === "sending"} class="inline-flex w-full items-center justify-center rounded-lg bg-gray-900 px-4 py-2.5 text-sm font-medium text-white hover:bg-gray-800 disabled:opacity-50">
{status === "sending" ? "Sending…" : "Send"}
</button>
</form>
{/if}Route
<script>
import ContactForm from "$lib/ContactForm.svelte";
</script>
<svelte:head><title>Contact</title></svelte:head>
<h1>Contact</h1>
<ContactForm />Progressive enhancement
Add action="https://formsubmit.app/f/YOUR_FORM_ID" method="POST" to the <form>. If JavaScript hasn't loaded, the browser posts normally and FormSubmit redirects to your thank-you page; once hydrated, the onsubmit handler takes over.
Svelte 4
Replace $state with plain let variables and onsubmit with on:submit|preventDefault. The fetch logic is identical.
Next steps
Add your domain to the allowlist, connect Slack and review spam settings.