Products Pricing About Login Get Started →
← All guides
/forms/guides/astro — static · island · hybrid

ASTRO
FORMS.

Add a codaForms form to your Astro site with zero JavaScript, a small client-side island, or a full React component — whichever fits the page.

● 3 minutes ● Beginner friendly

Prerequisites

Easiest

Embed script

Zero JavaScript authoring. Works with static builds.

Drop the embed script into any Astro page or component:

src/pages/contact.astro
---
import Layout from '../layouts/Layout.astro';
---

<Layout title="Contact">
  <h1>Contact Us</h1>

  <!-- codaForms embed - that's it! -->
  <script src="https://codasite.ai/forms-embed.js" data-form="cf_xxxxx"></script>
</Layout>
Custom styling

Astro component

Own the markup, submit to codaForms over fetch.

src/components/ContactForm.astro
---
interface Props {
  formId: string;
}

const { formId } = Astro.props;
---

<form id="contact-form" class="space-y-4">
  <input name="name" placeholder="Name" required />
  <input name="email" type="email" placeholder="Email" required />
  <textarea name="message" placeholder="Message" rows="4" required></textarea>
  <button type="submit">Send Message</button>
</form>

<div id="success-message" hidden>
  <p>Thanks! We'll be in touch soon.</p>
</div>

<script define:vars={{ formId }}>
  const form = document.getElementById('contact-form');
  const successMessage = document.getElementById('success-message');

  form.addEventListener('submit', async (e) => {
    e.preventDefault();

    const formData = new FormData(form);
    const data = {
      _formId: formId,
      name: formData.get('name'),
      email: formData.get('email'),
      message: formData.get('message'),
    };

    const res = await fetch('https://codasite.ai/forms/api/submit', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify(data),
    });

    const result = await res.json();

    if (result.success) {
      form.hidden = true;
      successMessage.hidden = false;
    } else {
      alert('Something went wrong. Please try again.');
    }
  });
</script>

Use it in any page:

---
import Layout from '../layouts/Layout.astro';
import ContactForm from '../components/ContactForm.astro';
---

<Layout title="Contact">
  <h1>Contact Us</h1>
  <ContactForm formId="cf_xxxxx" />
</Layout>
Interactive

React island

Use React when the form needs richer client state.

src/components/ContactForm.tsx
import { useState } from 'react';

export default function ContactForm({ formId }: { formId: string }) {
  const [status, setStatus] = useState<'idle' | 'submitting' | 'success'>('idle');

  async function handleSubmit(e: React.FormEvent<HTMLFormElement>) {
    e.preventDefault();
    setStatus('submitting');

    const formData = new FormData(e.currentTarget);

    const res = await fetch('https://codasite.ai/forms/api/submit', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({
        _formId: formId,
        name: formData.get('name'),
        email: formData.get('email'),
        message: formData.get('message'),
      }),
    });

    const result = await res.json();
    if (result.success) setStatus('success');
  }

  if (status === 'success') {
    return <p>Thanks! We'll be in touch soon.</p>;
  }

  return (
    <form onSubmit={handleSubmit}>
      <input name="name" placeholder="Name" required />
      <input name="email" type="email" placeholder="Email" required />
      <textarea name="message" placeholder="Message" required />
      <button disabled={status === 'submitting'}>
        {status === 'submitting' ? 'Sending...' : 'Send'}
      </button>
    </form>
  );
}
src/pages/contact.astro
---
import Layout from '../layouts/Layout.astro';
import ContactForm from '../components/ContactForm';
---

<Layout title="Contact">
  <h1>Contact Us</h1>
  <ContactForm client:load formId="cf_xxxxx" />
</Layout>
Ready to ship

Create your first form in under 2 minutes.

Free account, unlimited forms, 500 submissions/mo.