Skip to main content

Basic Astro Form

In Astro, you can use standard HTML forms that submit directly to Spike:
src/pages/contact.astro
---
// 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
---
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
---
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
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
---
import ReactForm from '../components/ReactForm';
---

<ReactForm client:load formSlug="YOUR_FORM_SLUG" />