Formular-ID

Nuxt

Eine Nuxt-Komponente, die vor der Hydration als einfaches Formular sendet und danach mit $fetch, mit der Formular-ID in der Runtime-Config. Funktioniert mit nuxt generate.

Nuxt rendert das Formular auf dem Server, daher ist das HTML, das im Browser ankommt, bereits ein funktionierendes Formular. Bis die App hydriert, sendet es wie jedes HTML-Formular; danach übernimmt die Komponente und zeigt das Ergebnis direkt an. Auf deinem Server läuft nichts, daher funktioniert es auch als statische Site, gebaut mit nuxt generate.

Um stattdessen aus einer Nuxt-Server-Route mit einem geheimen Schlüssel zu senden, siehe Vue.

Einrichten

  1. Lege die Formular-ID in der Runtime-Config ab

    nuxt.config.ts
    export default defineNuxtConfig({
      runtimeConfig: {
        public: {
          ikiformFormId: "frm_xxxxxxxxxxxxxxxx",
        },
      },
    });

    Überschreibe sie pro Umgebung mit NUXT_PUBLIC_IKIFORM_FORM_ID. Mit nuxt generate wird der Wert beim Build fest eingebaut, setze die Variable also in der Build-Umgebung.

  2. Füge die Komponente hinzu

    app/components/ContactForm.vue
    <script setup lang="ts">
    type Result =
      | { ok: true; id: string }
      | { ok: false; error: { message: string; fields?: Record<string, string[]> } };
    
    const config = useRuntimeConfig();
    const endpoint = `https://ikiform.com/f/${config.public.ikiformFormId}`;
    
    const status = ref<"idle" | "sending" | "sent" | "error">("idle");
    const errors = ref<Record<string, string[]>>({});
    const message = ref("");
    
    async function onSubmit(event: Event) {
      status.value = "sending";
      const result = await $fetch<Result>(endpoint, {
        method: "POST",
        headers: { Accept: "application/json" },
        body: new FormData(event.currentTarget as HTMLFormElement),
        ignoreResponseError: true,
      }).catch(() => null);
      if (result?.ok) {
        status.value = "sent";
        return;
      }
      errors.value = result?.error.fields ?? {};
      message.value = result?.error.message ?? "Couldn't reach the server. Try again.";
      status.value = "error";
    }
    </script>
    
    <template>
      <p v-if="status === 'sent'">Thanks, we got it.</p>
      <form v-else :action="endpoint" method="post" @submit.prevent="onSubmit">
        <label>
          Email
          <input name="email" type="email" autocomplete="email" required />
        </label>
        <p v-if="errors.email">{{ errors.email[0] }}</p>
        <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 />
        <p v-if="status === 'error' && !errors.email" role="alert">{{ message }}</p>
        <button :disabled="status === 'sending'">{{ status === "sending" ? "Sending" : "Send" }}</button>
      </form>
    </template>
    • Lege sie unter Nuxt 3 in components/ statt in app/components/ ab.
    • ignoreResponseError: true verhindert, dass $fetch bei 4xx wirft, sodass der Fehler-Envelope als Daten zurückkommt. Nur Netzwerkfehler landen in catch.
    • @submit.prevent existiert erst, wenn die App hydriert ist. Davor sendet der Browser an action und folgt _redirect.
  3. Verwende sie auf einer Seite

    app/pages/contact.vue
    <template>
      <ContactForm />
    </template>

Der Fallback braucht Server-Rendering oder Prerendering. Mit ssr: false ist die Seite leer, bis JavaScript läuft, ohne JavaScript gibt es also kein Formular zum Absenden.

Erlaubte Domains

Liste die Produktion plus die Preview-Hosts deines Deployment-Ziels auf.

Wo das Formular läuftZu Allowed domains hinzufügen
nuxt devlocalhost. Passt auf http://localhost:3000: Ikiform vergleicht nur den Hostnamen, nie den Port.
Deine Domainexample.com, was auch www.example.com abdeckt
Vercelyour-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.
Netlifyyour-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 Pagesyour-project.pages.dev. Preview-URLs wie abc123.your-project.pages.dev sind Subdomains davon und passen daher bereits.
  • Füge niemals vercel.app, netlify.app oder pages.dev allein hinzu. Jede Site auf diesem Host würde passen, auch die von anderen Leuten.

Test-Checkliste

  1. Starte nuxt dev mit localhost in den erlaubten Domains und sende ab. Die Dankesnachricht sollte das Formular ersetzen.
  2. Schalte JavaScript in den Dev-Tools deines Browsers aus, lade neu und sende ab. Lokal solltest du auf der Dankesseite von Ikiform landen, auf deiner https:-Domain auf /thanks.
  3. Sende bei definierten Feldern eine ungültige E-Mail ab und prüfe, dass die Meldung unter dem Feld erscheint.
  4. Starte nuxt generate, deploye die Ausgabe und sende vom Preview-Host ab.

Fehlerbehebung

Was du siehstWarumLösung
FetchError: [POST] "https://ikiform.com/f/...": 422ignoreResponseError fehlt, daher wirft $fetch bei FehlernFüge ignoreResponseError: true hinzu.
404 not_found, und die action endet auf /f/undefinedDie Formular-ID war beim Build der Site nicht gesetztSetze NUXT_PUBLIC_IKIFORM_FORM_ID in der Build-Umgebung oder den Standardwert in nuxt.config.ts.
Ohne JavaScript passiert nichtsDie Seite wird mit ssr: false clientseitig gerendertAktiviere Server-Rendering oder prerendere die Seite.
Einsendungen landen im Spamfilter mit origin_mismatchDie sendende Seite steht nicht in den erlaubten Domains des FormularsFüge den Host aus der Tabelle oben hinzu. Mit Reject bekommen dieselben Requests stattdessen 403 origin_not_allowed.