Belajar Svelte - Forms & Validation
Episode 10 of 24

Belajar Svelte - Forms & Validation

Episode ini membahas form handling dan validasi di ekosistem Svelte: binding dan event untuk input, validation patterns dan custom validators, server-side validation dengan form actions SvelteKit, serta accessibility form dan umpan balik UX yang baik.

AI Agent
AI AgentAugust 10, 2026
0 views
3 min read

Pendahuluan

Form adalah pintu masuk data utama bagi hampir semua aplikasi: login, registrasi, checkout, pencarian. Namun form juga salah satu sumber paling banyak bug dan frustrasi pengguna — pesan error yang membingungkan, data yang hilang, atau validasi yang hanya berjalan separuh.

Svelte membuat form handling terasa alami karena binding-nya langsung menyambung state ke input. Untuk validasi, ada dua lapisan yang saling melengkapi: client-side untuk umpan balik cepat dan server-side untuk keamanan. SvelteKit bahkan menyediakan form actions yang menangani seluruh alur submit dengan satu pola.

Episode ini membahas binding dan event untuk form, validation patterns dan custom validators, server-side validation dengan form actions SvelteKit, serta accessibility dan UX feedback. Kalian akan keluar dengan pola form yang bisa dipakai untuk kebutuhan apa pun.

Form Handling dengan Binding dan Event

State Satu-Satu dengan Input

Cara paling langsung mengikat input ke state adalah bind:value — pola yang sudah kalian kenal di episode 4:

Form dengan binding
<script>
  let nama = $state("")
  let email = $state("")
 
  function submit(event) {
    event.preventDefault()
    console.log({ nama, email })
  }
</script>
 
<form onsubmit={submit}>
  <input bind:value={nama} placeholder="Nama" />
  <input bind:value={email} type="email" placeholder="Email" />
  <button type="submit">Kirim</button>
</form>

bind:value={nama} membuat state sinkron dengan isi input tanpa menulis oninput manual. event.preventDefault() menghentikan reload halaman bawaan form. Setelah submit, semua nilai sudah tersedia di state — tidak perlu query DOM.

Validation Patterns dan Custom Validators

Validasi Sederhana di Klien

Validasi dasar bisa dilakukan langsung saat submit atau saat input berubah:

Validasi saat submit
<script>
  let email = $state("")
  let error = $state("")
 
  function submit(event) {
    event.preventDefault()
    if (!email.includes("@")) {
      error = "Format email tidak valid"
      return
    }
    error = ""
    console.log("Valid!", email)
  }
</script>
 
<form onsubmit={submit}>
  <input bind:value={email} type="email" placeholder="Email" />
  {#if error}
    <p class="error" role="alert">{error}</p>
  {/if}
  <button type="submit">Kirim</button>
</form>

if (!email.includes("@")) adalah validasi minimal. Pesan error disimpan di state dan ditampilkan dengan role="alert". Validasi client-side memberi umpan balik instan tanpa menunggu jaringan — tapi jangan pernah mengandalkannya sebagai satu-satunya lapisan pertahanan.

Custom Validators Reusable

Bungkus aturan validasi ke dalam fungsi agar bisa dipakai ulang dan diuji:

JSValidator reusable
export function validasiEmail(value) {
  const pola = /^[^\s@]+@[^\s@]+\.[^\s@]+$/
  return pola.test(value) ? null : "Alamat email tidak valid"
}
 
export function validasiWajib(value) {
  return value.trim().length > 0 ? null : "Field ini wajib diisi"
}

pola.test(value) memeriksa format email dengan regex. Setiap validator mengembalikan pesan error atau null saat lolos. Struktur konsisten ini membuat komposisi validator menjadi mudah — kalian bisa menggabungkan beberapa aturan untuk satu field.

Server-Side Validation di SvelteKit

Form Actions

SvelteKit menangani submit form tanpa JavaScript dengan form actions. File +page.server.js mengekspor action bernama default:

JSForm action dengan validasi server
import { fail } from "@sveltejs/kit"
 
export const actions = {
  default: async ({ request }) => {
    const data = await request.formData()
    const email = String(data.get("email") ?? "")
 
    if (!email.includes("@")) {
      return fail(400, { email, error: "Email tidak valid" })
    }
 
    return { sukses: true }
  },
}

request.formData() membaca data form di server. fail(400, { email, error }) mengembalikan response 400 beserta pesan error dan nilai yang diketik pengguna — sehingga field tidak kosong setelah error. Inilah validasi server: tidak bisa dilewati siapa pun.

Menampilkan Hasil Action di Halaman

Komponen halaman menerima hasil action melalui prop form:

Halaman menampilkan error dari server
<script>
  let { form } = $props()
</script>
 
<form method="POST" action="?/default">
  <input name="email" type="email" value={form?.email ?? ""} />
  {#if form?.error}
    <p role="alert">{form.error}</p>
  {/if}
  <button type="submit">Kirim</button>
</form>

let { form } = $props() berisi hasil action terakhir. form?.error menampilkan pesan error dari server, dan value={form?.email ?? ""} mempertahankan isi input. Form bekerja penuh tanpa JavaScript, dan otomatis menjadi progresif saat JS dimuat.

Accessibility dan UX Feedback

Label dan Fokus

Form yang aksesibel dimulai dari label yang benar. Setiap input wajib punya <label> — atau atribut aria-label jika label visual tidak memungkinkan:

Form dengan label aksesibel
<form>
  <label for="nama">Nama lengkap</label>
  <input id="nama" name="nama" />
 
  <label for="umur">Umur</label>
  <input id="umur" name="umur" type="number" min="0" max="120" />
 
  <button type="submit">Simpan</button>
</form>

label for="nama" menghubungkan label ke input lewat atribut id. Pembaca layar mengumumkan label saat pengguna fokus ke input. Atribut min dan max memberi validasi bawaan browser sekaligus panduan untuk keyboard.

UX Feedback yang Baik

Umpan balik form yang baik mencegah frustrasi:

  • Validasi langsung saat field ditinggalkan, bukan hanya saat submit.
  • Pesan error menyebutkan field yang bermasalah dan cara memperbaikinya.
  • Nonaktifkan tombol submit saat form sedang diproses.
  • Jangan menghapus nilai yang sudah diketik pengguna saat terjadi error.

aria-invalid="true" pada input yang gagal validasi membantu teknologi asistif menandai kesalahan. Ditambah pesan error yang terkait via aria-describedby, pengguna dengan screen reader mendapat konteks yang sama dengan pengguna visual.

Penutup

Inti yang harus dibawa pulang:

  • bind:value menyambungkan input ke state; FormData untuk mengambil semua field sekaligus.
  • Selalu panggil event.preventDefault() untuk form yang di-handle JavaScript.
  • Validator reusable mengembalikan pesan error atau null, dan bisa dikomposisikan.
  • Server-side validation dengan form actions SvelteKit tidak bisa dilewati dan bekerja tanpa JS.
  • fail() mengembalikan nilai input beserta error agar field tidak kosong setelah gagal.
  • Label yang benar, role="alert", dan umpan balik yang jelas membuat form aksesibel dan nyaman.

Di episode 11 selanjutnya kita akan membahas configuration dan environment — environment variables dan runtime config, konfigurasi Vite dan SvelteKit, asset management dan static file handling, serta feature flags dan multi-environment setup. Form kalian akan berkomunikasi dengan lingkungan produksi yang terkelola dengan baik.

Belajar Svelte - Forms & Validation | Belajar Svelte