# SvelteKit

> Halaman SvelteKit prerender yang formulirnya mengirim langsung ke Ikiform, ditingkatkan dengan fetch begitu JavaScript dimuat. Berfungsi dengan adapter-static.

Halaman ini tidak butuh server: `action` formulir adalah endpoint Ikiform Anda, jadi berfungsi di `adapter-static` dan host statis mana pun. Tanpa JavaScript, formulir mengirim dan redirect seperti HTML biasa; dengan JavaScript, formulir mengirim dengan `fetch` dan menampilkan hasilnya di tempat. Untuk mengirim lewat form action dengan secret key, lihat [Svelte](https://ikiform.com/id/docs/guides/svelte.md#sveltekit).

## Kenapa tidak memakai use:enhance

`use:enhance` milik SvelteKit hanya berfungsi dengan formulir `method="POST"` yang mengarah ke action yang didefinisikan di file `+page.server.js`. Ikiform adalah URL eksternal, jadi halaman ini memakai handler `onsubmit` kecil sebagai gantinya. Progressive enhancement-nya sama: formulir berfungsi lebih dulu, skrip menyempurnakannya.

## Penyiapan

### Langkah 1: Tambahkan environment variable publik

`.env`:

```bash
PUBLIC_IKIFORM_FORM_ID=frm_xxxxxxxxxxxxxxxx
PUBLIC_SITE_URL=https://example.com
```

`PUBLIC_SITE_URL` dipakai untuk redirect tanpa JavaScript. Saat prerendering, `page.url.origin` bernilai `http://sveltekit-prerender`, bukan domain Anda, jadi redirect tidak bisa dibuat darinya.

### Langkah 2: Prerender halamannya

`src/routes/contact/+page.js`:

```js
export const prerender = true;
```

### Langkah 3: Tambahkan formulir

`src/routes/contact/+page.svelte`:

```svelte
<script>
  import { PUBLIC_IKIFORM_FORM_ID, PUBLIC_SITE_URL } from "$env/static/public";

  const endpoint = `https://ikiform.com/f/${PUBLIC_IKIFORM_FORM_ID}`;

  let status = $state("idle");
  let errors = $state({});
  let message = $state("");

  async function onsubmit(event) {
    event.preventDefault();
    status = "sending";
    try {
      const response = await fetch(endpoint, {
        method: "POST",
        headers: { Accept: "application/json" },
        body: new FormData(event.currentTarget),
      });
      const result = await response.json();
      if (result.ok) {
        status = "sent";
        return;
      }
      errors = result.error.fields ?? {};
      message = result.error.message;
    } catch {
      message = "Couldn't reach the server. Try again.";
    }
    status = "error";
  }
</script>

{#if status === "sent"}
  <p>Thanks, we got it.</p>
{:else}
  <form action={endpoint} method="POST" {onsubmit}>
    <label>
      Email
      <input name="email" type="email" autocomplete="email" required aria-invalid={errors.email ? "true" : undefined} />
    </label>
    {#if errors.email}<p>{errors.email[0]}</p>{/if}
    <label>
      Message
      <textarea name="message" rows="5"></textarea>
    </label>
    <input type="hidden" name="_redirect" value={`${PUBLIC_SITE_URL}/contact/thanks`} />
    <input name="_gotcha" type="text" tabindex="-1" autocomplete="off" hidden />
    {#if status === "error" && !errors.email}<p role="alert">{message}</p>{/if}
    <button disabled={status === "sending"}>{status === "sending" ? "Sending" : "Send"}</button>
  </form>
{/if}
```

Tambahkan `src/routes/contact/thanks/+page.svelte` sebagai tujuan redirect.

## Jika Anda memakai CSP SvelteKit

Jika `kit.csp` dikonfigurasi, izinkan Ikiform untuk kiriman `fetch` maupun kiriman formulir biasa. Jika tidak, browser akan memblokir salah satunya.

`svelte.config.js`:

```js
const config = {
  kit: {
    csp: {
      directives: {
        "connect-src": ["self", "https://ikiform.com"],
        "form-action": ["self", "https://ikiform.com"],
      },
    },
  },
};

export default config;
```

## Domain yang diizinkan

Cantumkan domain produksi plus host pratinjau Anda.

| Tempat formulir berjalan | Tambahkan ke Domain yang diizinkan |
| --- | --- |
| `vite dev` | `localhost`. Ini cocok dengan `http://localhost:5173`: Ikiform hanya membandingkan nama host, tidak pernah port. |
| Domain Anda | `example.com`, yang juga mencakup `www.example.com` |
| Vercel | `your-project.vercel.app`. URL pratinjau seperti `your-project-git-branch-team.vercel.app` tidak cocok dengannya; tambahkan host pratinjau persisnya selama Anda menguji. |
| Cloudflare Pages | `your-project.pages.dev`. URL pratinjau seperti `abc123.your-project.pages.dev` adalah subdomainnya, jadi sudah cocok. |
| Netlify | `your-site.netlify.app`. Deploy preview (`deploy-preview-12--your-site.netlify.app`) dan branch deploy (`branch--your-site.netlify.app`) bukan subdomainnya, jadi tambahkan host pratinjau persisnya selama Anda menguji. |

- Jangan pernah menambahkan `vercel.app`, `pages.dev`, atau `netlify.app` saja. Setiap situs di host itu akan cocok, termasuk milik orang lain.

## Daftar periksa pengujian

1. Jalankan dev server dengan `localhost` di domain yang diizinkan, lalu kirim. Pesan terima kasih seharusnya menggantikan formulir.
2. Jalankan `vite build` dan pastikan output-nya berisi `contact.html` yang sudah memuat formulir.
3. Matikan JavaScript, muat ulang, lalu kirim. Di domain `https:` Anda, Anda seharusnya mendarat di `/contact/thanks`.
4. Kirim dari deploy pratinjau dan pastikan tidak ditandai spam.

## Pemecahan masalah

| Yang Anda lihat | Alasan | Solusi |
| --- | --- | --- |
| Build gagal dengan pesan bahwa `PUBLIC_IKIFORM_FORM_ID` tidak diekspor oleh `$env/static/public` | Variabelnya tidak diatur di tempat build berjalan | Tambahkan ke `.env` secara lokal dan ke pengaturan build host Anda. |
| Tidak terjadi apa-apa saat dikirim, dan console menampilkan error CORS tentang header `x-sveltekit-action` | `use:enhance` di formulir yang mengirim ke luar aplikasi Anda | Hapus dan pertahankan handler `onsubmit`. |
| Browser menolak mengirim formulir karena `form-action` atau `connect-src` | `kit.csp` tidak mencantumkan Ikiform | Tambahkan `https://ikiform.com` ke kedua direktif. |
| Tanpa JavaScript, Anda mendarat di halaman terima kasih Ikiform | `PUBLIC_SITE_URL` memakai `http:`, atau domainnya tidak ada di domain yang diizinkan | Gunakan alamat `https:` Anda dan tambahkan domainnya. |
| Kiriman masuk ke filter spam dengan `origin_mismatch` | Halaman yang mengirim tidak ada di domain yang diizinkan formulir | Tambahkan host dari tabel di atas. Jika **Reject** dipilih, kiriman yang sama mendapat `403 origin_not_allowed`. |

## Terkait

- [Svelte](https://ikiform.com/id/docs/guides/svelte.md): Komponen Svelte 5 dan server action.
- [Astro](https://ikiform.com/id/docs/guides/astro.md): Formulir tanpa JavaScript, dengan peningkatan opsional.
- [Domain yang diizinkan](https://ikiform.com/id/docs/spam.md#allowed-domains): Cara origin dicocokkan, dan apa akibat ketidakcocokan.
- [Error](https://ikiform.com/id/docs/errors.md): Setiap kode error, beserta penyebab dan solusinya.
