# Gatsby

> Ein Gatsby-Formular, das schon im statischen HTML vor der Hydration funktioniert, danach mit fetch erweitert wird, plus eine Gatsby Function für Posts mit einem geheimen Schlüssel.

Gatsby rendert jede Seite beim Build zu HTML. Gib dem Formular eine echte `action`, dann sendet es, bevor React hydriert oder falls das Bundle nicht lädt; nach der Hydration sendet die Komponente per `fetch` und zeigt das Ergebnis direkt an.

## Aus dem Browser senden

### Schritt 1: Füge die Formular-ID hinzu

`.env.production`:

```bash
GATSBY_IKIFORM_FORM_ID=frm_xxxxxxxxxxxxxxxx
```

Gatsby gibt nur Variablen, die mit `GATSBY_` beginnen, an Browser-Code weiter und fügt sie beim Build inline ein. Füge dieselbe Zeile für `gatsby develop` zu `.env.development` hinzu.

### Schritt 2: Füge die Komponente hinzu

`src/components/contact-form.js`:

```jsx
import * as React from "react";

const endpoint = `https://ikiform.com/f/${process.env.GATSBY_IKIFORM_FORM_ID}`;

export default function ContactForm() {
  const [status, setStatus] = React.useState("idle");
  const [message, setMessage] = React.useState("");

  async function onSubmit(event) {
    event.preventDefault();
    setStatus("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) return setStatus("sent");
      setMessage(result.error.message);
    } catch {
      setMessage("Couldn't reach the server. Try again.");
    }
    setStatus("error");
  }

  if (status === "sent") return <p>Thanks, we got it.</p>;

  return (
    <form action={endpoint} method="post" onSubmit={onSubmit}>
      <label>
        Email
        <input name="email" type="email" autoComplete="email" required />
      </label>
      <label>
        Message
        <textarea name="message" rows={5} />
      </label>
      <input type="hidden" name="_redirect" value="https://example.com/thanks/" />
      <input name="_gotcha" type="text" tabIndex={-1} autoComplete="off" hidden />
      {status === "error" ? <p role="alert">{message}</p> : null}
      <button disabled={status === "sending"}>Send</button>
    </form>
  );
}
```

Rendere sie auf einer beliebigen Seite, zum Beispiel `src/pages/contact.js`, und füge `src/pages/thanks.js` für die Weiterleitung ohne JavaScript hinzu. Für Feldfehler und die Menschlichkeitsprüfung des SDK erweitere sie wie im [React-Guide](https://ikiform.com/de/docs/guides/react.md#component).

## Aus einer Gatsby Function senden

Eine Gatsby Function kann den Post mit einem geheimen Schlüssel weiterleiten, der die erlaubten Domains und die Menschlichkeitsprüfung überspringt. Sie läuft nur auf Hosts, die Gatsby Functions unterstützen, etwa Netlify mit dem Gatsby-Adapter; ein rein statischer Host wie GitHub Pages kann sie nicht ausführen. Speichere `IKIFORM_FORM_ID` und einen geheimen Schlüssel mit dem Scope `submissions:write` als `IKIFORM_SECRET_KEY`, ohne das Präfix `GATSBY_`, damit sie nie den Browser erreichen.

`src/api/contact.js`:

```js
export default async function handler(req, res) {
  if (req.method !== "POST") return res.status(405).end();
  const response = await fetch(`https://ikiform.com/f/${process.env.IKIFORM_FORM_ID}`, {
    method: "POST",
    headers: {
      "Content-Type": "application/json",
      Accept: "application/json",
      Authorization: `Bearer ${process.env.IKIFORM_SECRET_KEY}`,
    },
    body: JSON.stringify(req.body),
  });
  res.status(response.status).json(await response.json());
}
```

Gatsby parst JSON- und form-encoded Bodies in `req.body`, daher kann die Seite auf beide Arten an `/api/contact` senden. Alle Posts teilen sich dann für Rate-Limits die IP-Adresse deiner Function; siehe [Von einem Server](https://ikiform.com/de/docs/guides/server.md).

## Erlaubte Domains

Das gilt für Posts aus dem Browser. Posts aus der Gatsby Function nutzen einen geheimen Schlüssel und überspringen die Prüfung.

| Wo das Formular läuft | Zu Allowed domains hinzufügen |
| --- | --- |
| `gatsby develop` und `gatsby serve` | `localhost`. Passt auf `http://localhost:8000` und `http://localhost:9000`: Ikiform vergleicht nur den Hostnamen, nie den Port. |
| Deine Domain | `example.com`, was auch `www.example.com` abdeckt |
| 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. |
| Cloudflare Pages | `your-project.pages.dev`. Preview-URLs wie `abc123.your-project.pages.dev` sind Subdomains davon und passen daher bereits. |

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

## Test-Checkliste

1. Starte `gatsby build` und öffne `public/contact/index.html`. Die `action` des Formulars sollte mit deiner Formular-ID enden, nicht mit `undefined`.
2. Starte `gatsby serve` mit `localhost` in den erlaubten Domains und sende ab. Die Dankesnachricht sollte das Formular ersetzen.
3. Schalte JavaScript aus, lade neu und sende ab. Lokal solltest du die Dankesseite von Ikiform sehen, auf deiner `https:`-Domain `/thanks/`.
4. Wenn du die Function nutzt, sende in einer Deploy-Preview an `/api/contact` und prüfe, dass die Einsendung in Ikiform erscheint.

## Fehlerbehebung

| Was du siehst | Warum | Lösung |
| --- | --- | --- |
| `404 not_found`, und die action endet auf `/f/undefined` | Die Variable hat nicht das Präfix `GATSBY_` oder war beim Build nicht gesetzt | Nenne sie `GATSBY_IKIFORM_FORM_ID` und baue neu. |
| Die Function antwortet mit `403 forbidden` | Der geheime Schlüssel stammt aus einem anderen Workspace, wurde widerrufen oder hat kein `submissions:write` | Erstelle einen geheimen Schlüssel mit diesem Scope. |
| `/api/contact` gibt nach dem Deploy `404` zurück | Der Host liefert nur statische Dateien aus | Deploye auf einen Host mit Gatsby Functions oder nutze die Browser-Version. |
| 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

- [React](https://ikiform.com/de/docs/guides/react.md): Lade-, Fehler- und Erfolgszustände.
- [Von einem Server](https://ikiform.com/de/docs/guides/server.md): Sende mit einem geheimen API-Schlüssel.
- [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.
