Basic Astro Form
In Astro, you can use standard HTML forms that submit directly to Spike:src/pages/contact.astro
Copy
---
// Contact page
---
<html>
<head>
<title>Contact Us</title>
</head>
<body>
<form action="https://spike.ac/api/f/YOUR_FORM_SLUG" method="POST">
<input type="text" name="_gotcha" style="display:none" />
<label for="name">Name</label>
<input type="text" id="name" name="name" required />
<label for="email">Email</label>
<input type="email" id="email" name="email" required />
<label for="message">Message</label>
<textarea id="message" name="message" required></textarea>
<button type="submit">Send Message</button>
</form>
</body>
</html>
Astro Component
Create a reusable contact form component:src/components/ContactForm.astro
Copy
---
interface Props {
formSlug: string;
redirectUrl?: string;
}
const { formSlug, redirectUrl } = Astro.props;
---
<form
action={`https://spike.ac/api/f/${formSlug}`}
method="POST"
class="contact-form"
>
{redirectUrl && (
<input type="hidden" name="_next" value={redirectUrl} />
)}
<input type="text" name="_gotcha" style="display:none" />
<div class="form-group">
<label for="name">Name</label>
<input type="text" id="name" name="name" required />
</div>
<div class="form-group">
<label for="email">Email</label>
<input type="email" id="email" name="email" required />
</div>
<div class="form-group">
<label for="message">Message</label>
<textarea id="message" name="message" rows="4" required></textarea>
</div>
<button type="submit">Send</button>
</form>
Client-Side Submission
For AJAX submissions without page reload:src/components/AjaxForm.astro
Copy
---
interface Props {
formSlug: string;
}
const { formSlug } = Astro.props;
---
<form id="ajax-form" data-form-slug={formSlug}>
<input type="email" name="email" placeholder="[email protected]" required />
<textarea name="message" placeholder="Your message" required></textarea>
<button type="submit">Send</button>
<p id="form-status" style="display:none"></p>
</form>
<script>
const form = document.getElementById('ajax-form') as HTMLFormElement;
const status = document.getElementById('form-status');
const formSlug = form.dataset.formSlug;
form.addEventListener('submit', async (e) => {
e.preventDefault();
const data = new FormData(form);
try {
const res = await fetch(`https://spike.ac/api/f/${formSlug}`, {
method: 'POST',
body: data,
headers: { 'Accept': 'application/json' }
});
if (res.ok) {
status.textContent = 'Message sent!';
status.style.display = 'block';
form.reset();
} else {
throw new Error('Failed');
}
} catch (err) {
status.textContent = 'Error sending message';
status.style.display = 'block';
}
});
</script>
With React Island
Use React components in Astro with client directives:src/components/ReactForm.tsx
Copy
import { useState } from 'react';
export default function ReactForm({ formSlug }: { formSlug: string }) {
const [status, setStatus] = useState<'idle' | 'loading' | 'success' | 'error'>('idle');
const handleSubmit = async (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault();
setStatus('loading');
const form = e.currentTarget;
const data = new FormData(form);
try {
const res = await fetch(`https://spike.ac/api/f/${formSlug}`, {
method: 'POST',
body: data,
headers: { 'Accept': 'application/json' }
});
if (res.ok) {
setStatus('success');
form.reset();
} else {
setStatus('error');
}
} catch {
setStatus('error');
}
};
return (
<form onSubmit={handleSubmit}>
<input type="email" name="email" required />
<textarea name="message" required />
<button type="submit" disabled={status === 'loading'}>
{status === 'loading' ? 'Sending...' : 'Send'}
</button>
{status === 'success' && <p>Thanks!</p>}
{status === 'error' && <p>Error, try again</p>}
</form>
);
}
src/pages/contact.astro
Copy
---
import ReactForm from '../components/ReactForm';
---
<ReactForm client:load formSlug="YOUR_FORM_SLUG" />