← All Guides
Astro Forms
Add forms to your Astro site with zero JavaScript or interactive islands.
3 minutes
Beginner friendly
Prerequisites
- A codaForms account (sign up free)
- An Astro project
Option 1: Embed Script (Easiest)
Zero JavaScript, works with static builds.
Just add the embed script to 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> Option 2: Astro Component
Custom styling with client-side JavaScript.
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
class="w-full p-3 rounded-lg bg-gray-800 border border-gray-700"
/>
<input
name="email"
type="email"
placeholder="Email"
required
class="w-full p-3 rounded-lg bg-gray-800 border border-gray-700"
/>
<textarea
name="message"
placeholder="Message"
rows="4"
required
class="w-full p-3 rounded-lg bg-gray-800 border border-gray-700"
></textarea>
<button
type="submit"
class="w-full p-3 bg-blue-600 rounded-lg hover:bg-blue-700"
>
Send Message
</button>
</form>
<div id="success-message" class="hidden p-6 bg-green-500/10 rounded-lg text-center">
<p class="text-green-400">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'),
};
try {
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.classList.add('hidden');
successMessage.classList.remove('hidden');
} else {
alert('Something went wrong. Please try again.');
}
} catch (error) {
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> Option 3: React Island
Use React for complex interactive forms.
If you need React-level interactivity, create an island component:
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>