TutorialVueSvelteSvelteKit
Form Integration Guide for Vue and Svelte
7 min read
Vue 3 (Composition API)
Basic integration
<script setup>
import { ref } from "vue";
const status = ref("idle"); // idle | submitting | success | error
const errorMsg = ref("");
async function handleSubmit(event) {
status.value = "submitting";
errorMsg.value = "";
try {
const response = await fetch("https://inputhaven.com/api/v1/submit", {
method: "POST",
body: new FormData(event.target),
headers: { Accept: "application/json" },
});
if (response.ok) {
status.value = "success";
event.target.reset();
} else {
const data = await response.json().catch(() => null);
errorMsg.value = data?.error || "Something went wrong.";
status.value = "error";
}
} catch {
errorMsg.value = "Network error. Please try again.";
status.value = "error";
}
}
</script>
<template>
<div v-if="status === 'success'">
<h3>Message sent!</h3>
<p>Thank you. We'll respond soon.</p>
<button @click="status = 'idle'">Send another</button>
</div>
<form v-else @submit.prevent="handleSubmit">
<input type="hidden" name="_form_id" value="your-form-id" />
<!-- Honeypot -->
<div style="position: absolute; left: -9999px" aria-hidden="true">
<input type="text" name="_gotcha" tabindex="-1" autocomplete="off" />
</div>
<div>
<label for="name">Name</label>
<input type="text" id="name" name="name" required />
</div>
<div>
<label for="email">Email</label>
<input type="email" id="email" name="email" required />
</div>
<div>
<label for="message">Message</label>
<textarea id="message" name="message" required rows="4" />
</div>
<p v-if="errorMsg" role="alert" class="error">{{ errorMsg }}</p>
<button type="submit" :disabled="status === 'submitting'">
{{ status === "submitting" ? "Sending..." : "Send Message" }}
</button>
</form>
</template>Nuxt 3
In Nuxt, the same component works as-is. Place it in components/ContactForm.vue and use it anywhere:
<template>
<ContactForm />
</template>No server middleware or API routes needed.
Svelte
Basic integration
<script>
let status = "idle"; // idle | submitting | success | error
let errorMsg = "";
async function handleSubmit(event) {
status = "submitting";
errorMsg = "";
try {
const response = await fetch("https://inputhaven.com/api/v1/submit", {
method: "POST",
body: new FormData(event.target),
headers: { Accept: "application/json" },
});
if (response.ok) {
status = "success";
event.target.reset();
} else {
const data = await response.json().catch(() => null);
errorMsg = data?.error || "Something went wrong.";
status = "error";
}
} catch {
errorMsg = "Network error. Please try again.";
status = "error";
}
}
</script>
{#if status === "success"}
<div>
<h3>Message sent!</h3>
<p>Thank you. We'll respond soon.</p>
<button on:click={() => (status = "idle")}>Send another</button>
</div>
{:else}
<form on:submit|preventDefault={handleSubmit}>
<input type="hidden" name="_form_id" value="your-form-id" />
<!-- Honeypot -->
<div style="position: absolute; left: -9999px;" aria-hidden="true">
<input type="text" name="_gotcha" tabindex="-1" autocomplete="off" />
</div>
<div>
<label for="name">Name</label>
<input type="text" id="name" name="name" required />
</div>
<div>
<label for="email">Email</label>
<input type="email" id="email" name="email" required />
</div>
<div>
<label for="message">Message</label>
<textarea id="message" name="message" required rows="4" />
</div>
{#if errorMsg}
<p role="alert" class="error">{errorMsg}</p>
{/if}
<button type="submit" disabled={status === "submitting"}>
{status === "submitting" ? "Sending..." : "Send Message"}
</button>
</form>
{/if}SvelteKit
In SvelteKit, you can also use progressive enhancement with SvelteKit's use:enhance:
<script>
// Same as above — SvelteKit doesn't require any special handling
// for client-side form submission to external APIs
</script>SvelteKit's form actions are for server-side processing within your app. For external form backends like InputHaven, use client-side fetch as shown above.
TypeScript support
Both Vue and Svelte support TypeScript. The form data types are simple:
interface SubmissionResponse {
success: boolean;
message?: string;
submissionId?: string;
}
interface ErrorResponse {
error: string;
}For full type safety with InputHaven's API, generate a typed client from our OpenAPI specification at /openapi.yaml.
Using @inputhaven/js
For framework-agnostic integration, use our JavaScript package:
npm install @inputhaven/jsimport { InputHaven } from "@inputhaven/js";
const client = new InputHaven("your-form-id");
const result = await client.submit({
name: "Jane Doe",
email: "jane@example.com",
message: "Hello from Vue/Svelte!",
});
if (result.success) {
// Submission successful
} else {
// Handle error: result.error
}This works in Vue, Svelte, or any JavaScript environment.