Lista de espera para um novo SaaS7 min de leitura
Como adicionar um formulário de lista de espera em HTML puro a qualquer landing page
Uma página de lista de espera completa em HTML puro, sem build, que funciona com o JavaScript desligado, mostra um agradecimento na própria página quando carrega e tem plano B quando precisa.
Escreva um formulário HTML normal que envia o e-mail ao seu endpoint do Ikiform e redireciona para uma página de agradecimento, para que funcione em qualquer host sem nenhum JavaScript. Depois adicione um script pequeno que envia o mesmo formulário com fetch e mostra o agradecimento na página, e que devolve o controle ao envio simples sempre que não consegue terminar, como quando a verificação humana está ligada.
Uma página de lista de espera não precisa de framework, etapa de build nem servidor. Quatro arquivos estáticos e um backend de formulários dão conta, e a página carrega mais rápido do que qualquer coisa que um criador de sites exporta. Esta página monta tudo, camada por camada, para que cada camada funcione sozinha.
O que importa sem etapa de build
- O formulário é a integração. Um
<form>simples com umactioné toda a conexão com o Ikiform. Sem pacote, sem chave de API, nada para compilar. - JavaScript é uma melhoria, não um requisito. A página deve coletar inscrições quando o script está bloqueado, lento ou quebrado. Cada camada abaixo se soma à anterior e volta para ela quando falha.
- O script nunca pode deixar alguém preso. Se o
fetchfalhar ou o Ikiform pedir uma verificação humana que ofetchnão consegue rodar, o script devolve o formulário para o navegador enviar do jeito normal. - A hospedagem pode ser em qualquer lugar. GitHub Pages, Netlify, Cloudflare Pages, um bucket S3 ou uma hospedagem compartilhada servem estes arquivos como estão.
| Camada | O que ela adiciona | Se faltar |
|---|---|---|
| Formulário HTML | Envia o e-mail, redireciona para thanks.html | Nada funciona, então ele vem primeiro |
| CSS | Uma página legível nos modos claro e escuro | Um formulário simples, mas funcionando |
| Script | Agradecimento na página, erros de campo, sem trocar de página | O envio simples e o redirecionamento |
- Com o JavaScript desligado, o navegador envia o formulário, o Ikiform redireciona com 303 para a sua URL de _redirect e o visitante vê o thanks.html.
- Com o script carregado, ele envia com fetch e pede JSON. O Ikiform responde 201 ou um erro, e o script mostra o resultado na mesma página.
- Se o fetch falhar, ou se o Ikiform responder challenge_required, o script chama form.submit().
- Esse envio simples passa pela página de verificação humana do Ikiform, se necessário, e termina no thanks.html.
Os arquivos
A página
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Acme is almost here</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<main>
<h1>Acme is almost here</h1>
<p>Invoices that chase themselves. Leave your email and we'll invite you first.</p>
<form id="waitlist" action="https://ikiform.com/f/frm_xxxxxxxxxxxxxxxx" method="post">
<label for="email">Email</label>
<div class="row">
<input id="email" name="email" type="email" autocomplete="email" required placeholder="you@company.com">
<button>Join the waitlist</button>
</div>
<input type="hidden" name="_redirect" value="https://example.com/thanks.html">
<input name="_gotcha" type="text" tabindex="-1" autocomplete="off" hidden>
<p class="message" role="status" aria-live="polite"></p>
</form>
</main>
<script src="waitlist.js" defer></script>
</body>
</html>- O
_redirectmanda o navegador parathanks.htmldepois de um envio simples. Ele só funciona com uma URLhttps:em um dos Allowed domains do formulário, então adicione seu domínio lá. Redirect after submit, nas configurações do formulário, faz o mesmo sem o input oculto. - O input
_gotchaé o honeypot. As pessoas nunca o veem; bots que preenchem todos os inputs são armazenados como spam. aria-live="polite"faz os leitores de tela anunciarem as mensagens do script sem mover o foco.- A tag script usa
defer, então roda depois que o formulário existe e nunca impede a página de ser desenhada.
A melhoria
const form = document.getElementById("waitlist");
const message = form.querySelector(".message");
const button = form.querySelector("button");
const key = crypto.randomUUID();
form.addEventListener("submit", async (event) => {
event.preventDefault();
button.disabled = true;
message.textContent = "Joining…";
try {
const response = await fetch(form.action, {
method: "POST",
headers: { Accept: "application/json", "Idempotency-Key": key },
body: new FormData(form),
});
const result = await response.json();
if (result.ok) {
form.replaceChildren(Object.assign(document.createElement("p"), {
textContent: "You're on the list. We'll email you on launch day.",
role: "status",
}));
return;
}
if (result.error.code === "challenge_required") {
form.submit();
return;
}
const wait = response.headers.get("Retry-After");
message.textContent = wait
? `Too many tries. Wait ${wait} seconds and try again.`
: result.error.fields?.email?.[0] ?? result.error.message;
} catch {
form.submit();
return;
}
button.disabled = false;
});Accept: application/jsonfaz o Ikiform responder com JSON em vez de um redirecionamento, para o script poder mostrar o resultado no lugar.- A chave de idempotência é criada uma vez por carregamento de página. Um clique duplo, ou uma nova tentativa depois de uma resposta lenta, armazena uma única inscrição.
challenge_requiredsignifica que a verificação de spam do formulário está como Ikiform e ofetchnão enviou token.form.submit()envia do jeito tradicional, o que mostra a página de verificação do Ikiform e depois a página de agradecimento. Ele não dispara o eventosubmitde novo, então não há loop.- Um erro de rede segue o mesmo plano B. Se o Ikiform estava inacessível, o envio simples mostra o erro do próprio navegador, o que ainda é mais honesto que um botão travado.
- Um
429lê oRetry-Aftere diz quanto tempo esperar.
A página de agradecimento
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>You're on the list</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<main>
<h1>You're on the list</h1>
<p>We'll email you the day Acme opens. Nothing before that.</p>
<p><a href="/">Back to the page</a></p>
</main>
</body>
</html>Os estilos
body { margin: 0; font: 16px/1.5 system-ui, sans-serif; color: #111; background: #fff; }
main { max-width: 34rem; margin: 0 auto; padding: 18vh 1rem 4rem; }
h1 { font-size: 2.25rem; line-height: 1.1; margin: 0 0 0.75rem; }
label { display: block; font-weight: 600; margin: 1.5rem 0 0.375rem; }
.row { display: flex; flex-wrap: wrap; gap: 0.5rem; }
input[type="email"] { flex: 1 1 14rem; font: inherit; padding: 0.625rem 0.75rem; border: 1px solid #ccc; border-radius: 0.5rem; }
button { font: inherit; padding: 0.625rem 1rem; border: 0; border-radius: 0.5rem; background: #111; color: #fff; cursor: pointer; }
button:disabled { opacity: 0.6; cursor: progress; }
.message { min-height: 1.5em; margin: 0.5rem 0 0; }
@media (prefers-color-scheme: dark) {
body { color: #eee; background: #111; }
input[type="email"] { background: #1b1b1b; color: inherit; border-color: #444; }
button { background: #eee; color: #111; }
}example.com, atualize o _redirect e o link de volta. O Ikiform ignora um _redirect que não consegue seguir e mostra a própria página de agradecimento hospedada, então uma divergência nunca faz você perder uma inscrição.Deploy e teste
Coloque os quatro arquivos numa pasta e suba-a para qualquer host estático. Depois adicione o domínio do host em Allowed domains: por exemplo username.github.io, your-site.netlify.app ou o seu próprio domínio. Enquanto trabalha localmente, adicione localhost também; sirva a pasta com qualquer servidor estático, já que uma página aberta como URL file:// não envia origem.
Confira se funciona
- Em Allowed domains do formulário, adicione o domínio do seu site, mais
localhostenquanto desenvolve. Deixe vazio para aceitar envios de qualquer lugar. - Desligue o JavaScript no navegador e inscreva-se. Você cai no
thanks.html. - Ligue-o de novo e inscreva-se outra vez. O formulário dá lugar à frase de agradecimento sem sair da página.
- Defina Spam check como Ikiform e inscreva-se mais uma vez. A página vai para a verificação e depois para o
thanks.html. - Abra Submissions no painel. O novo envio está lá com todos os campos que você enviou.
- Envie com o honeypot preenchido,
_gotcha=bot. Ele ainda recebe uma resposta de sucesso, mas o envio é armazenado como spam e não dispara e-mail nem webhook.
Pegadinhas
| Problema | O que fazer |
|---|---|
| O navegador cai na página de agradecimento do Ikiform em vez da sua | O _redirect é ignorado, a menos que o domínio dele esteja em Allowed domains e ele seja uma URL https:. Adicione o domínio, ou defina Redirect after submit nas configurações do formulário. |
403 origin_not_allowed, ou todo envio cai no spam | O domínio da página não está em Allowed domains do formulário. Adicione-o, mais localhost para testes locais. Subdomínios de um domínio listado são permitidos. |
| Envios de uma página aberta como arquivo são recusados ou marcados como spam | Uma página file:// não envia origem. Sirva a pasta com um servidor estático local e adicione localhost em Allowed domains. |
| A frase de agradecimento nunca aparece, mas as inscrições chegam | O script não carregou: confira o caminho no atributo src, ou se o host serve arquivos .js. O envio simples continua funcionando, e é por isso que as inscrições chegam. |
402 test_limit_reached | O workspace não tem plano e já usou os 50 testes gratuitos de localhost deste mês. Escolha um plano ou use Send test no painel. |
A documentação de formulários HTML lista todos os campos reservados, e o modelo de lista de espera gera uma versão com nome e função. Exportar a lista e o e-mail de lançamento estão no guia principal.
Perguntas
Sim. Um formulário HTML simples envia o e-mail ao seu endpoint do Ikiform, e o Ikiform redireciona o navegador para a sua página de agradecimento. O JavaScript só adiciona a mensagem na página.
Em qualquer lugar que sirva arquivos estáticos: GitHub Pages, Netlify, Cloudflare Pages, S3 ou uma hospedagem compartilhada. O formulário envia ao Ikiform, então o host nunca roda código nenhum.
O Ikiform responde 403 challenge_required. O script deste guia pega isso e envia o formulário do jeito tradicional, o que mostra a página de verificação e depois a sua página de agradecimento.
Não. O Ikiform não tem páginas de formulário hospedadas. Você coloca o formulário no seu próprio site, em qualquer criador de sites ou framework, e o aponta para o seu endpoint do Ikiform.
Não, não há plano gratuito. Testar é grátis: você pode enviar 50 envios de teste por mês a partir de localhost antes de pagar. Para entrar no ar, o Pro começa em US$ 6/mês para 500 envios, ou você pode pagar uma vez 10.000 envios por US$ 49 ou 50.000 envios por US$ 149, com créditos que nunca expiram.
Travou em algum passo ou quer um guia para o seu caso? Envie um e-mail para hello@ikiform.com.