# SvelteKit

> Eine vorgerenderte SvelteKit-Seite, deren Formular direkt an Ikiform sendet und mit fetch erweitert wird, sobald JavaScript lädt. Funktioniert mit adapter-static.

Diese Seite braucht keinen Server: Die `action` des Formulars ist dein Ikiform-Endpunkt, daher funktioniert sie mit `adapter-static` und auf jedem statischen Host. Ohne JavaScript sendet sie und leitet weiter wie einfaches HTML; mit JavaScript sendet sie per `fetch` und zeigt das Ergebnis direkt an. Um stattdessen über eine Form Action mit einem geheimen Schlüssel zu senden, siehe [Svelte](https://ikiform.com/de/docs/guides/svelte.md#sveltekit).

## Warum nicht use:enhance

`use:enhance` von SvelteKit funktioniert nur mit `method="POST"`-Formularen, die auf Actions in einer `+page.server.js`-Datei zeigen. Ikiform ist eine externe URL, daher nutzt die Seite stattdessen einen kleinen `onsubmit`-Handler. Die Progressive Enhancement ist dieselbe: Das Formular funktioniert zuerst, das Skript verbessert es.

## Einrichten

### Schritt 1: Füge öffentliche Umgebungsvariablen hinzu

`.env`:

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

`PUBLIC_SITE_URL` ist für die Weiterleitung ohne JavaScript. Beim Prerendering ist `page.url.origin` `http://sveltekit-prerender`, nicht deine Domain, daher lässt sich die Weiterleitung nicht daraus bauen.

### Schritt 2: Prerendere die Seite

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

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

### Schritt 3: Füge das Formular hinzu

`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}
```

Füge eine `src/routes/contact/thanks/+page.svelte` als Weiterleitungsziel hinzu.

## Wenn du die CSP von SvelteKit nutzt

Wenn `kit.csp` konfiguriert ist, erlaube Ikiform sowohl für den `fetch`-Post als auch für den einfachen Formular-Post. Sonst blockiert der Browser das eine oder das andere.

`svelte.config.js`:

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

export default config;
```

## Erlaubte Domains

Liste die Produktion plus deine Preview-Hosts auf.

| Wo das Formular läuft | Zu Allowed domains hinzufügen |
| --- | --- |
| `vite dev` | `localhost`. Passt auf `http://localhost:5173`: Ikiform vergleicht nur den Hostnamen, nie den Port. |
| Deine Domain | `example.com`, was auch `www.example.com` abdeckt |
| Vercel | `your-project.vercel.app`. Preview-URLs wie `your-project-git-branch-team.vercel.app` passen nicht darauf; füge den exakten Preview-Host hinzu, während du eine Preview testest. |
| Cloudflare Pages | `your-project.pages.dev`. Preview-URLs wie `abc123.your-project.pages.dev` sind Subdomains davon und passen daher bereits. |
| Netlify | `your-site.netlify.app`. Deploy Previews (`deploy-preview-12--your-site.netlify.app`) und Branch Deploys (`branch--your-site.netlify.app`) sind keine Subdomains davon, füge also den exakten Preview-Host hinzu, während du eine testest. |

- Füge niemals `vercel.app`, `pages.dev` oder `netlify.app` allein hinzu. Jede Site auf diesem Host würde passen, auch die von anderen Leuten.

## Test-Checkliste

1. Starte den Dev-Server mit `localhost` in den erlaubten Domains und sende ab. Die Dankesnachricht sollte das Formular ersetzen.
2. Starte `vite build` und prüfe, dass die Ausgabe `contact.html` mit dem Formular darin enthält.
3. Schalte JavaScript aus, lade neu und sende ab. Auf deiner `https:`-Domain solltest du auf `/contact/thanks` landen.
4. Sende von einem Preview-Deploy ab und prüfe, dass die Einsendung nicht als Spam markiert ist.

## Fehlerbehebung

| Was du siehst | Warum | Lösung |
| --- | --- | --- |
| Der Build schlägt fehl mit der Meldung, dass `PUBLIC_IKIFORM_FORM_ID` nicht von `$env/static/public` exportiert wird | Die Variable ist dort, wo der Build läuft, nicht gesetzt | Füge sie lokal zu `.env` und zu den Build-Einstellungen deines Hosts hinzu. |
| Beim Absenden passiert nichts, und die Konsole zeigt einen CORS-Fehler zum `x-sveltekit-action`-Header | `use:enhance` auf einem Formular, das außerhalb deiner App sendet | Entferne es und behalte den `onsubmit`-Handler. |
| Der Browser verweigert das Senden des Formulars wegen `form-action` oder `connect-src` | `kit.csp` listet Ikiform nicht auf | Füge `https://ikiform.com` zu beiden Direktiven hinzu. |
| Ohne JavaScript landest du auf der Dankesseite von Ikiform | `PUBLIC_SITE_URL` ist `http:`, oder die Domain steht nicht in den erlaubten Domains | Verwende deine `https:`-Adresse und füge die Domain hinzu. |
| Einsendungen landen im Spamfilter mit `origin_mismatch` | Die sendende Seite steht nicht in den erlaubten Domains des Formulars | Füge den Host aus der Tabelle oben hinzu. Mit **Reject** bekommen dieselben Requests stattdessen `403 origin_not_allowed`. |

## Verwandte Themen

- [Svelte](https://ikiform.com/de/docs/guides/svelte.md): Eine Svelte-5-Komponente und eine Server Action.
- [Astro](https://ikiform.com/de/docs/guides/astro.md): Ein Formular ohne JavaScript, optional erweitert.
- [Erlaubte Domains](https://ikiform.com/de/docs/spam.md#allowed-domains): Wie Origins abgeglichen werden und was eine Abweichung bewirkt.
- [Fehler](https://ikiform.com/de/docs/errors.md): Jeder Fehlercode mit Ursache und Lösung.
