Lista de espera para um novo SaaS7 min de leitura
Como adicionar uma lista de espera a um site no Framer
Duas formas de coletar inscrições na lista de espera no Framer: o formulário do Framer com webhook para o Ikiform, ou um code component que mantém os limites por visitante e as verificações de spam.
Os formulários do Framer não conseguem enviar para uma URL externa, mas conseguem mandar cada envio para um webhook, então o caminho sem código é o formulário do Framer com o seu endpoint do Ikiform como webhook. Para um lançamento público, use um pequeno code component: o navegador envia diretamente, então o limite de taxa por visitante, os domínios permitidos e o honeypot do Ikiform continuam funcionando.
O Framer é onde muitas páginas de pré-lançamento são construídas, porque a página em si sai numa tarde. O formulário da lista de espera é onde a coisa fica menos óbvia. O bloco de formulário do Framer não consegue mudar o action, então você escolhe entre dois caminhos que se comportam de forma diferente quando o tráfego real chega.
O que muda no Framer
- Sem action personalizado. Um formulário do Framer envia para os destinos do Framer: o armazenamento dele, e-mail ou um Webhook. Um webhook é o único jeito de chegar a um backend externo sem código.
- Os webhooks vêm dos servidores do Framer, não do visitante. O Ikiform vê os endereços IP do Framer e nenhum header
Origin. Isso desliga três proteções: domínios permitidos, o limite de taxa por visitante e a verificação humana. - Novas tentativas. O Framer trata qualquer coisa diferente de um
2xxcomo falha e tenta de novo até cinco vezes. Ele não segue redirecionamentos. O Ikiform responde a um envio JSON com201, então uma inscrição armazenada é um sucesso na primeira vez. - Code components são React de verdade. Eles rodam no navegador do visitante no site publicado, então um formulário dentro de um deles envia como qualquer outra página web.
- No caminho do webhook, o visitante envia o formulário do Framer e os servidores do Framer encaminham os campos ao Ikiform como JSON.
- O Ikiform vê os servidores do Framer como remetente, então não consegue diferenciar os visitantes nem conferir em que site o formulário está.
- Com o code component, o navegador do visitante envia direto ao Ikiform.
- O Ikiform vê o IP de cada visitante e o seu domínio, então todas as proteções se aplicam.
Escolha um caminho
| Item | Formulário do Framer com webhook | Code component |
|---|---|---|
| Configuração | Dois campos na barra lateral direita | Um arquivo de código, colado uma vez |
| Estilo | Os estilos e estados de formulário do Framer | Estilos inline que você edita, ou suas próprias classes |
| Limite de taxa por visitante | Não. Todos os envios compartilham os IPs do Framer | Sim, 5 por minuto cada |
| Domínios permitidos | Deixe vazio | Defina com seus domínios do Framer e personalizados |
| Honeypot | Não | Sim |
| Verificação humana | Desligada | Desligada, porque envia com fetch |
| Rastreamento de origem | Nenhuma | ?ref= da URL, ou um padrão por instância |
O caminho do webhook serve para uma página que você divulga para algumas centenas de pessoas. Se você está prestes a divulgar a página num lugar grande, use o code component: uma rajada de inscrições de bots pelos servidores do Framer só pode ser barrada pela pontuação de spam.
Caminho 1: o formulário do Framer com webhook
- Adicione à página um formulário com um input Email. Nas configurações do input, defina o nome dele como
email. O Framer envia o nome de cada input como chave JSON, e o Ikiform usaemailpara o reply-to e a verificação de domínios descartáveis. - Selecione o formulário. Ao lado de Send To, escolha Add… › Webhook e cole o seu endpoint,
https://ikiform.com/f/frm_xxxxxxxxxxxxxxxx. O signing secret é opcional; o Ikiform não confere a assinatura do Framer. - No Ikiform, deixe Allowed domains vazio e a verificação de spam desligada. Com qualquer um dos dois ligado, todo envio do Framer é sinalizado ou recusado, porque não traz origem e não consegue rodar uma verificação de navegador.
- Defina o limite de taxa como Relaxed, ou Custom no dia do lançamento. O Standard conta todos os envios do Framer como um único visitante, e o formulário inteiro tem limite de 300 por minuto.
- Estilize o estado de sucesso do formulário no Framer. Ele aparece quando o Ikiform responde
201.
Caminho 2: um code component
No painel Assets, abra Code, crie um arquivo de código chamado WaitlistSignup e cole isto. Arraste-o para a página e defina Form ID no painel de propriedades.
import { addPropertyControls, ControlType } from "framer";
import { type CSSProperties, type FormEvent, useRef, useState } from "react";
const box: CSSProperties = {
font: "inherit",
padding: "12px 14px",
borderRadius: 10,
border: "1px solid rgba(0, 0, 0, 0.15)",
};
interface Props {
formId: string;
buttonLabel: string;
joinedText: string;
source: string;
}
export default function WaitlistSignup({ formId, buttonLabel, joinedText, source }: Props) {
const [status, setStatus] = useState<"idle" | "sending" | "joined">("idle");
const [error, setError] = useState("");
const key = useRef("");
async function onSubmit(event: FormEvent<HTMLFormElement>) {
event.preventDefault();
const body = new FormData(event.currentTarget);
const ref = new URLSearchParams(window.location.search).get("ref");
body.set("ref", (ref || source).slice(0, 40));
key.current ||= crypto.randomUUID();
setStatus("sending");
setError("");
try {
const response = await fetch(`https://ikiform.com/f/${formId}`, {
method: "POST",
headers: { Accept: "application/json", "Idempotency-Key": key.current },
body,
});
const result = await response.json();
if (result.ok) return setStatus("joined");
setError(result.error.fields?.email?.[0] ?? result.error.message);
} catch {
setError("Couldn't reach the server. Try again.");
}
setStatus("idle");
}
if (status === "joined") return <p role="status" style={{ margin: 0 }}>{joinedText}</p>;
return (
<form onSubmit={onSubmit} style={{ display: "flex", flexWrap: "wrap", gap: 8, width: "100%" }}>
<input
name="email"
type="email"
placeholder="you@company.com"
aria-label="Email"
autoComplete="email"
required
style={{ ...box, flex: "1 1 220px" }}
/>
<input name="_gotcha" type="text" tabIndex={-1} autoComplete="off" hidden />
<button type="submit" disabled={status === "sending"} style={{ ...box, cursor: "pointer" }}>
{status === "sending" ? "Joining" : buttonLabel}
</button>
{error ? <p role="alert" style={{ flexBasis: "100%", margin: 0 }}>{error}</p> : null}
</form>
);
}
addPropertyControls(WaitlistSignup, {
formId: { type: ControlType.String, title: "Form ID", defaultValue: "frm_xxxxxxxxxxxxxxxx" },
buttonLabel: { type: ControlType.String, title: "Button", defaultValue: "Join the waitlist" },
joinedText: { type: ControlType.String, title: "Joined text", defaultValue: "You're on the list." },
source: { type: ControlType.String, title: "Source", defaultValue: "framer" },
});- O painel de propriedades expõe o texto do botão, o texto de inscrito e uma Source padrão. Coloque uma instância no hero com
heroe outra no rodapé comfooter, e você vai ver qual converte. - Um
?ref=na URL da página vence o padrão, então os links de campanha se marcam sozinhos. - A chave de idempotência é criada no primeiro envio e reutilizada nas novas tentativas da mesma visita, então um toque duplo no celular armazena uma única inscrição.
windowsó é lido dentro do handler de envio. O Framer renderiza os componentes no servidor primeiro, e mexer emwindowdurante a renderização quebraria isso.
Configurações do Ikiform para o componente
Adicione estes campos na aba Fields do formulário, para o e-mail ser obrigatório e o ref ser permitido:
{
"version": 1,
"title": "Waitlist",
"fields": [
{ "id": "email", "type": "email", "label": "Email", "required": true },
{ "id": "ref", "type": "text", "label": "Source", "validation": { "max_length": 40 } }
]
}- Em Allowed domains, adicione
your-project.framer.appe o seu domínio personalizado. Nunca adicioneframer.appsozinho; todo site do Framer passaria a corresponder. - Mantenha a verificação de spam desligada. O componente envia com
fetche não busca um token, então um formulário com a verificação ligada responde403 challenge_required. - Mantenha os limites de taxa Standard. Agora cada visitante é contado separadamente.
Confira se funciona
- Publique o site. Webhooks e code components só enviam a partir do site publicado, não do preview do editor.
- Envie o formulário no seu domínio
framer.appe de novo no seu domínio personalizado. - Abra Submissions no painel. O novo envio está lá com todos os campos que você enviou.
- Para o componente, abra a página com
?ref=teste confira se a inscrição mostrarefcomotest. - Confira a caixa de entrada de cada destinatário das notificações. Cada um recebe um e-mail com os campos, e Reply vai para quem enviou quando o formulário tem um campo
email. - Não chegou nada? Mude o filtro de status do painel para Spam. Spam recebe a mesma resposta de sucesso de um envio real.
Pegadinhas
| Problema | O que fazer |
|---|---|
Toda inscrição via webhook está no spam com origin_mismatch | Allowed domains está definido, e os envios do servidor do Framer não têm origem. Limpe-o para este formulário ou troque para o code component. |
| O Framer acusa entregas de webhook com falha | O Ikiform recusou o envio: 403 challenge_required com a verificação de spam ligada, ou 422 porque o nome de um input não é email. Corrija a causa; as novas tentativas falham do mesmo jeito. |
429 rate_limited | Um visitante enviou mais do que o limite de taxa do formulário permite. O Standard permite 5 por minuto por visitante. Espere o Retry-After, ou escolha Relaxed ou Custom para formulários que enviam a partir de um único IP compartilhado. |
O componente mostra Unknown field "ref" | A definição de campos não lista ref. Adicione-o como na definição acima. |
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. |
A configuração completa do Framer, incluindo a versão de formulário de contato, está no guia do Framer na documentação. Exportar a lista e enviar o e-mail de lançamento estão no guia principal.
Perguntas
Não como action de formulário. Os formulários do Framer enviam para os destinos do próprio Framer, e um deles é um webhook para qualquer URL https. Use isso para o Ikiform, ou renderize seu próprio formulário com um code component.
No caminho do webhook, o Framer envia a partir dos servidores dele sem header Origin, então um formulário com Allowed domains definido marca todo envio como origin_mismatch. Deixe Allowed domains vazio em formulários via webhook.
O Framer diz que os envios de formulário são ilimitados, então as inscrições via webhook não batem em nenhum limite do Framer. O caminho do code component nem usa os formulários do Framer.
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.