Episode ini membahas form dan interaksi di Astro: penanganan form di halaman statis dan server, interaksi sisi klien dengan komponen yang di-hydrate, validasi UI dan progressive enhancement, serta pengiriman form melalui fungsi serverless atau endpoint.

Form adalah jembatan antara pengunjung dan pemilik situs: newsletter, kontak, pendaftaran, atau pencarian. Episode 10 ini membahas cara membangun form di Astro dengan pola yang benar — dari form HTML sederhana hingga form interaktif yang mengirim data ke fungsi serverless.
Kunci dari bab ini adalah progressive enhancement: form harus tetap berfungsi meskipun JavaScript gagal dimuat. Kalian akan belajar membangun form yang bekerja dalam dua lapisan — HTML murni untuk dasar, dan interaksi JavaScript untuk pengalaman yang lebih halus.
Episode ini juga membahas validasi UI, umpan balik untuk pengguna, dan cara menghubungkan form ke endpoint serverless.
Form paling dasar tidak butuh JavaScript sama sekali. Gunakan atribut action dan method standar HTML:
<form action="/api/pendaftaran" method="POST">
<label for="email">Email</label>
<input id="email" name="email" type="email" required />
<button type="submit">Daftar</button>
</form>Form di atas mengirim data dengan method="POST" ke /api/pendaftaran. Ini adalah fondasi progressive enhancement — meskipun tidak ada satu baris JavaScript, form tetap berfungsi.
Untuk menerima kiriman form, buat endpoint di src/pages/api. Di mode server, kalian bisa menulis handler dengan fungsi post:
export async function post({ request }) {
const formData = await request.formData();
const email = formData.get("email");
if (!email) {
return new Response("Email wajib diisi", { status: 400 });
}
return new Response("Terima kasih sudah mendaftar", { status: 200 });
}Endpoint di atas membaca request.formData() dari kiriman form, memvalidasi email, dan mengembalikan respons. Pola request.formData() hanya tersedia di mode server — di mode statis kalian butuh fungsi serverless dari platform hosting.
Untuk umpan balik real-time — validasi saat mengetik, indikator loading, atau pesan sukses tanpa reload — bungkus form dalam komponen yang di-hydrate:
export default function FormDaftar() {
const [status, setStatus] = useState("idle");
async function kirim(event) {
event.preventDefault();
setStatus("loading");
const body = new FormData(event.currentTarget);
const res = await fetch("/api/pendaftaran", { method: "POST", body });
setStatus(res.ok ? "sukses" : "gagal");
}
return (
<form onSubmit={kirim}>
<input name="email" type="email" required />
<button type="submit" disabled={status === "loading"}>
{status === "loading" ? "Mengirim..." : "Daftar"}
</button>
</form>
);
}Komponen ini di-hydrate dengan client:load di halaman Astro. Form memakai fetch untuk mengirim data tanpa reload, dan menampilkan status loading saat pengiriman.
Gunakan validasi di dua tempat: atribut HTML seperti required, type="email", dan minlength di sisi browser, lalu validasi ulang di server. Atribut HTML adalah lapisan pertama, validasi server adalah lapisan yang sebenarnya — jangan pernah hanya mengandalkan JavaScript.
<input name="email" type="email" required minlength="5" />Atribut type="email" dan required membuat browser menolak input kosong atau format salah sebelum form dikirim — tanpa satu baris JavaScript.
Di komponen interaktif, tampilkan pesan error spesifik di dekat field, bukan sekadar alert. Saat JavaScript mati, pengguna tetap mendapat respons dari server lewat halaman yang dimuat ulang dengan atribut action standar.
Seperti biasa di Astro: tambahkan JavaScript hanya jika benar-benar menambah nilai. Form kontak sederhana mungkin cukup dengan HTML murni. Form pendaftaran dengan validasi real-time layak memakai komponen yang di-hydrate.
Untuk situs statis, endpoint /api tidak tersedia di server sendiri. Solusinya adalah fungsi serverless dari platform hosting. Contoh pada Netlify:
# netlify/functions/pendaftaran.mjs
export default async (req) => {
const data = await req.json();
return new Response("OK", { status: 200 });
};Fungsi serverless berjalan di edge platform hosting dan bisa menerima kiriman form dari situs statis. Kalian bisa memakai Vercel Functions, Netlify Functions, atau Cloudflare Workers sesuai hosting.
Data form harus pergi ke suatu tempat: email, database, atau layanan pihak ketiga seperti Formspree. Pilih layanan yang sesuai beban dan kebutuhan privasi kalian — keputusan ini juga berkaitan dengan keamanan yang akan dibahas di episode 12.
Warning
Jangan pernah menempatkan logika penyimpanan data langsung di komponen klien. Data sensitif harus melewati endpoint server atau fungsi serverless untuk validasi dan perlindungan.
Episode 10 membangun jembatan antara pengunjung dan pemilik situs: form HTML murni yang bekerja tanpa JavaScript, endpoint API di mode server, komponen form interaktif dengan hydration, validasi dua lapisan dengan progressive enhancement, serta pengiriman form melalui fungsi serverless.
Inti yang harus dibawa pulang:
action dan method bekerja tanpa JavaScript.src/pages/api menerima kiriman dengan request.formData().Di episode 11 selanjutnya kita akan membahas content management dan CMS: integrasi headless CMS seperti Contentful, Sanity, atau Strapi, content sourcing dari Git-based CMS, preview mode dan build-time content updates, serta workflow authoring dan manajemen metadata. Konten kalian akan dikelola tim seperti produksi sungguhan.