Astro Forms
Astro Forms
Section titled “Astro Forms”Astro is great for static sites. Here’s how to wire up a contact form with Formkove.
Simple Form
Section titled “Simple Form”In any .astro file:
---// No server-side code needed. Formkove handles everything client-side---
<form id="contact-form"> <input type="text" name="name" placeholder="Full name" required /> <input type="email" name="email" placeholder="Email address" required /> <textarea name="message" placeholder="Your message" required></textarea> <button type="submit">Send Message</button></form>
<div id="form-result"></div>
<script> const form = document.getElementById("contact-form"); const result = document.getElementById("form-result");
form?.addEventListener("submit", async (e) => { e.preventDefault();
const formData = new FormData(form); const data = Object.fromEntries(formData);
result.textContent = "Sending...";
try { const res = await fetch("https://app.formkove.com/api/forms/YOUR_FORM_ID/submissions", { method: "POST", headers: { "Content-Type": "application/json", "Accept": "application/json" }, body: JSON.stringify(data) });
const json = await res.json();
if (res.ok) { result.textContent = "Message sent!"; form.reset(); } else { result.textContent = json.error || "Something went wrong."; } } catch (err) { result.textContent = "Network error. Please try again."; } });</script>With Validation (HTML5 + JS)
Section titled “With Validation (HTML5 + JS)”---// Full example with custom validation styling---
<style> .form-group { margin-bottom: 1rem; } input, textarea { width: 100%; padding: 0.5rem; border: 1px solid #ccc; } .error { color: red; font-size: 0.875rem; }</style>
<form id="contact-form" novalidate> <div class="form-group"> <input type="text" name="name" placeholder="Full name" required /> </div>
<div class="form-group"> <input type="email" name="email" placeholder="Email address" required /> </div>
<div class="form-group"> <textarea name="message" placeholder="Your message" required ></textarea> </div>
<button type="submit">Send Message</button> <p id="result"></p></form>
<script> const form = document.getElementById("contact-form"); const result = document.getElementById("result");
form?.addEventListener("submit", async (e) => { e.preventDefault();
if (!form.checkValidity()) { form.classList.add("was-validated"); return; }
const formData = new FormData(form); const data = Object.fromEntries(formData);
result.textContent = "Sending...";
try { const res = await fetch("https://app.formkove.com/api/forms/YOUR_FORM_ID/submissions", { method: "POST", headers: { "Content-Type": "application/json", "Accept": "application/json" }, body: JSON.stringify(data) });
const json = await res.json();
if (res.ok) { result.textContent = "Sent! We'll be in touch."; form.reset(); } else { result.textContent = json.error || "Something went wrong."; } } catch (err) { result.textContent = "Network error. Please try again."; } });</script>Key Points
Section titled “Key Points”- The form ID goes in the fetch URL, not as a hidden field
- Use
Content-Type: application/jsonwith fetch novalidateon the form lets you handle validation in JS- The
<script>tag runs client-side, with no SSR needed for the form submission - Works with Astro’s view transitions if you use
astro:page-loadinstead ofDOMContentLoaded