ID do formulário

Nuxt

Um componente Nuxt que envia como um formulário comum antes da hidratação e com $fetch depois, com o id do formulário na runtime config. Funciona com nuxt generate.

O Nuxt renderiza o formulário no servidor, então o HTML que chega ao navegador já é um formulário funcional. Até o app hidratar, ele envia como qualquer formulário HTML; depois disso, o componente assume e mostra o resultado no lugar. Nada roda no seu servidor, então também funciona como site estático gerado com nuxt generate.

Para enviar a partir de uma server route do Nuxt com uma chave secreta, veja Vue.

Configuração

  1. Coloque o id do formulário na runtime config

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

    Sobrescreva por ambiente com NUXT_PUBLIC_IKIFORM_FORM_ID. Com nuxt generate, o valor é fixado no build, então defina a variável no ambiente de build.

  2. Adicione o componente

    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>
    • No Nuxt 3, coloque em components/ em vez de app/components/.
    • ignoreResponseError: true impede que o $fetch lance exceção em 4xx, então o envelope de erro volta como dado. Só falhas de rede chegam ao catch.
    • @submit.prevent só existe depois que o app hidrata. Antes disso, o navegador envia para o action e segue o _redirect.
  3. Use em uma página

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

O fallback precisa de renderização no servidor ou pré-renderização. Com ssr: false, a página fica vazia até o JavaScript rodar, então não existe formulário para enviar sem ele.

Domínios permitidos

Liste a produção mais os hosts de preview de onde você faz deploy.

Onde o formulário rodaAdicione em Allowed domains
nuxt devlocalhost. Ele corresponde a http://localhost:3000: o Ikiform compara só o nome do host, nunca a porta.
Seu domínioexample.com, que também cobre www.example.com
Vercelyour-project.vercel.app. URLs de preview como your-project-git-branch-team.vercel.app não correspondem a ele; adicione o host exato do preview enquanto testa um.
Netlifyyour-site.netlify.app. Deploy previews (deploy-preview-12--your-site.netlify.app) e branch deploys (branch--your-site.netlify.app) não são subdomínios dele, então adicione o host exato do preview enquanto testa um.
Cloudflare Pagesyour-project.pages.dev. URLs de preview como abc123.your-project.pages.dev são subdomínios dele, então já correspondem.
  • Nunca adicione vercel.app, netlify.app ou pages.dev sozinhos. Todo site naquele host corresponderia, inclusive os de outras pessoas.

Checklist de teste

  1. Rode nuxt dev com localhost nos domínios permitidos e envie. A mensagem de agradecimento deve substituir o formulário.
  2. Desative o JavaScript nas ferramentas de desenvolvedor do navegador, recarregue e envie. Você deve chegar à página de agradecimento do Ikiform localmente, ou a /thanks no seu domínio https:.
  3. Envie um e-mail inválido com os campos definidos e confirme que a mensagem aparece abaixo do campo.
  4. Rode nuxt generate, faça deploy do resultado e envie a partir do host de preview.

Solução de problemas

O que você vêPor quêCorreção
FetchError: [POST] "https://ikiform.com/f/...": 422Falta ignoreResponseError, então o $fetch lança exceção em errosAdicione ignoreResponseError: true.
404 not_found, e o action termina em /f/undefinedO id do formulário não estava definido quando o site foi geradoDefina NUXT_PUBLIC_IKIFORM_FORM_ID no ambiente de build, ou o padrão em nuxt.config.ts.
Sem JavaScript, nada aconteceA página é renderizada no cliente com ssr: falseAtive a renderização no servidor ou pré-renderize a página.
Os envios chegam no filtro de spam com origin_mismatchA página que enviou não está nos domínios permitidos do formulárioAdicione o host da tabela acima. Com Reject escolhido, os mesmos envios recebem 403 origin_not_allowed.