Formulario de contacto en un sitio estático6 min de lectura
Añade un formulario de contacto en Netlify sin Netlify Forms
Usa un formulario de contacto en un sitio de Netlify sin Netlify Forms: sin detección en el build, envíos JSON desde React y un formulario aparte para las deploy previews.
Quita data-netlify del formulario y apunta su action a tu endpoint de Ikiform; así Netlify sirve la página y nunca toca el envío. Los formularios de React y Vue no necesitan una copia oculta en HTML y pueden enviar JSON. Define otro id de formulario para las deploy previews en netlify.toml, porque los hosts de preview no coinciden con tu sitio en Allowed domains.
Qué cambia en Netlify
Netlify es el único gran host estático con una función de formularios integrada. Netlify Forms lee tu HTML desplegado después de cada build, encuentra los formularios marcados con data-netlify o netlify y acepta envíos a tu propio sitio para esos formularios. Cualquier otro envío a una página estática falla, y en Netlify falla con 404, no con 405:
curl -i -X POST -d "message=hello" https://your-site.netlify.app/contact/HTTP/2 404
server: Netlify
cache-status: "Netlify Edge"; fwd=method; fwd-status=404
content-length: 0Ese 404 hace que la gente busque una página que falta y que no falta. Significa que ningún formulario detectado coincidió con el envío. Si usar Netlify Forms o no es otra cuestión, y la comparativa entre Ikiform y Netlify Forms la responde con honestidad: si tu sitio va a vivir siempre en Netlify y el formulario es HTML plano, Netlify Forms da menos trabajo. Esta página es para los demás casos.
| Tu situación | Por qué estorba Netlify Forms |
|---|---|
| El formulario lo renderiza React, Vue o Svelte | El build analiza HTML estático, así que un formulario renderizado en el cliente necesita un duplicado oculto para que Netlify lo encuentre |
Quieres enviar JSON desde fetch | Netlify Forms solo acepta cuerpos urlencoded y multipart |
| Puede que el sitio se mude a otro host | El formulario envía a tu propio dominio, así que solo funciona mientras Netlify sirva ese dominio |
| Los mensajes deben llegar a un webhook que verificas | Ikiform firma cada entrega con cabeceras de Standard Webhooks y la reintenta |
- Con Netlify Forms, Netlify analiza el HTML de cada despliegue en busca de formularios marcados con data-netlify.
- Un formulario que encontró acepta envíos a tu propio sitio, y Netlify los guarda.
- Un formulario que no encontró, como uno renderizado con JavaScript sin copia oculta, recibe 404 en cada envío.
- Con Ikiform, no hay nada que encontrar en el build.
- El formulario envía directamente a ikiform.com, como HTML o JSON, e Ikiform lo guarda y te envía un email.
Saca un formulario de Netlify Forms
- Quita del formulario
data-netlifyonetlify,netlify-honeypotydata-netlify-recaptcha, y cualquier input ocultoform-name. Si lo dejas,form-namese guarda como un campo más. - Borra la copia estática oculta de un formulario de JavaScript, como la de abajo. Netlify solo la necesitaba para detectar el formulario.
- Pon tu endpoint de Ikiform en el
action, o en la URL delfetch. El antiguoaction="/thanks"pasa al ajuste Redirect after submit del formulario. - Descarga primero los envíos anteriores desde la página Forms de Netlify. No se mueven solos.
<form name="contact" netlify hidden>
<input name="name" />
<input name="email" />
<textarea name="message"></textarea>
</form>Deja form detection activado si otros formularios del sitio siguen usando Netlify Forms. La documentación de Netlify dice que desactivar la detección es solo para sitios que no usan Netlify Forms. Cuando ya nada lo use, desactivarlo te ahorra un análisis en cada despliegue.
Qué sustituye a los ajustes antispam de Netlify
Netlify pasa Akismet por los envíos y ofrece netlify-honeypot y reCAPTCHA. En Ikiform, el honeypot sigue funcionando si pones en Honeypot field del formulario el nombre que ya usas, como bot-field. La puntuación de spam se aplica a cada envío sin configurar nada, y Spam check en Ikiform sustituye reCAPTCHA por Cloudflare Turnstile sin claves. Quita el div de reCAPTCHA al cambiar.
Un formulario de React que envía JSON
Sin Netlify Forms de por medio, un formulario renderizado en el cliente no necesita duplicado y puede enviar JSON, lo que conserva los tipos y te permite limpiar los valores antes. Este además registra qué contexto de Netlify generó la página:
import { type FormEvent, useState } from "react";
const ENDPOINT = `https://ikiform.com/f/${import.meta.env.VITE_IKIFORM_FORM_ID}`;
interface Message {
name: string;
email: string;
message: string;
context: string;
}
export function ContactForm() {
const [sent, setSent] = useState(false);
const [error, setError] = useState("");
async function onSubmit(event: FormEvent<HTMLFormElement>) {
event.preventDefault();
const data = new FormData(event.currentTarget);
const body: Message & Record<string, string> = {
name: String(data.get("name") ?? "").trim(),
email: String(data.get("email") ?? "").trim(),
message: String(data.get("message") ?? "").trim(),
context: import.meta.env.VITE_CONTEXT ?? "local",
_gotcha: String(data.get("_gotcha") ?? ""),
};
setError("");
const response = await fetch(ENDPOINT, {
method: "POST",
headers: { "Content-Type": "application/json", Accept: "application/json" },
body: JSON.stringify(body),
}).catch(() => null);
const result = await response?.json().catch(() => null);
if (result?.ok) return setSent(true);
const fields: Record<string, string[]> = result?.error?.fields ?? {};
setError(Object.values(fields)[0]?.[0] ?? result?.error?.message ?? "Couldn't send. Try again.");
}
if (sent) return <p role="status">Got it. I'll answer from my own inbox, usually the same day.</p>;
return (
<form onSubmit={onSubmit}>
<label htmlFor="name">Name</label>
<input id="name" name="name" autoComplete="name" required />
<label htmlFor="email">Email</label>
<input id="email" name="email" type="email" autoComplete="email" required />
<label htmlFor="message">Message</label>
<textarea id="message" name="message" rows={6} minLength={10} required />
<input name="_gotcha" type="text" tabIndex={-1} autoComplete="off" hidden />
{error ? <p role="alert">{error}</p> : null}
<button>Send</button>
</form>
);
}Content-Type: application/jsonle dice a Ikiform que lea JSON;Accept: application/jsonpide una respuesta en JSON.VITE_CONTEXTes un nombre de ejemplo. Netlify defineCONTEXTdurante el build; exponlo a Vite en tu comando de build, o quita el campo.- El honeypot se envía como una clave JSON. Mantenlo en el cuerpo aunque el input esté oculto, para que los bots que lo rellenan se sigan delatando.
Las deploy previews tienen su propio formulario
Las deploy previews viven en hosts como deploy-preview-12--your-site.netlify.app. Eso no es un subdominio de your-site.netlify.app, así que no coincide en Allowed domains, y los envíos desde previews acaban en spam. Dale a las previews y a los branch deploys su propio formulario en netlify.toml, y define la cabecera Content-Security-Policy en el mismo archivo:
[build]
command = "npm run build"
publish = "dist"
[context.production.environment]
VITE_IKIFORM_FORM_ID = "frm_xxxxxxxxxxxxxxxx"
[context.deploy-preview.environment]
VITE_IKIFORM_FORM_ID = "frm_yyyyyyyyyyyyyyyy"
[context.branch-deploy.environment]
VITE_IKIFORM_FORM_ID = "frm_yyyyyyyyyyyyyyyy"
[[headers]]
for = "/*"
[headers.values]
Content-Security-Policy = "default-src 'self'; form-action 'self' https://ikiform.com; connect-src 'self' https://ikiform.com"Deja vacío Allowed domains del formulario de preview, o añade el host de preview exacto mientras pruebas. El formulario de producción lleva tu dominio y your-site.netlify.app. Las cabeceras de netlify.toml se aplican a todos los contextos de despliegue; si prefieres un archivo _headers, tiene que acabar en el directorio de publicación.
netlify.app por sí solo a Allowed domains. Coincidiría cualquier sitio de Netlify, incluidas las copias de tu formulario en sitios de otras personas.Comprueba que funciona
- Abre una deploy preview, envía y confirma que el mensaje llega al formulario de preview.
- Haz merge y envía en producción. Confirma que llega al formulario de producción y que la página Forms de Netlify no muestra nada nuevo.
- Revisa la bandeja de entrada de cada destinatario de notificaciones. Cada uno recibe un email con los campos, y Reply va a quien envió el formulario cuando este tiene un campo
email. - Envía con el honeypot relleno,
_gotcha=bot. Sigue recibiendo una respuesta de éxito, pero el envío se guarda como spam y no manda ningún email ni webhook. - ¿No llegó nada? Cambia el filtro de estado del panel a Spam. El spam recibe la misma respuesta de éxito que un envío real.
Problemas habituales
| Problema | Qué hacer |
|---|---|
404 al enviar | El envío fue a tu propio sitio. Puede que un build en caché aún tenga el action antiguo, o que una llamada fetch siga enviando a /. Busca form-name en el directorio de publicación. |
| Los envíos siguen apareciendo en Netlify | Algún formulario de la página sigue enviando a tu propio sitio con data-netlify o netlify, así que Netlify sigue detectándolo y guardándolo. Quita el atributo, pon Ikiform en el action y vuelve a desplegar. |
404 not_found de Ikiform solo en las previews | El contexto de deploy preview no tiene VITE_IKIFORM_FORM_ID, así que la URL termina en undefined. Añádelo en [context.deploy-preview.environment]. |
403 origin_not_allowed, o todos los envíos acaban en spam | El dominio de la página no está en Allowed domains del formulario. Añádelo, más localhost para las pruebas locales. Los subdominios de un dominio de la lista están permitidos. |
422 files_not_supported | El formulario tiene un <input type="file"> con un archivo elegido. La subida de archivos aún no está disponible; quita el input o pide un enlace. |
Preguntas
Sí. Netlify solo gestiona los formularios que detectó en el build. Un formulario sin data-netlify cuyo action apunta a otro endpoint lo sirve Netlify como cualquier página, y envía directamente a ese endpoint.
Netlify responde con 404 a un POST a una página estática salvo que Netlify Forms haya detectado ese formulario en el HTML desplegado. Marca el formulario para Netlify Forms o apunta su action a otro backend.
Sí. La detección es por formulario, según el atributo. Mantén data-netlify en los formularios que deba gestionar Netlify y deja activada la detección de formularios.
Se quedan en Netlify, no en Ikiform. Descárgalos desde la página Forms del sitio antes de desactivar la detección de formularios, y guarda el archivo con tus registros.
Todavía no. Un envío que contiene un archivo se rechaza con 422 files_not_supported. Pide en su lugar un enlace al archivo, como la URL de una carpeta compartida.
¿Te atascaste en un paso o quieres una guía para tu caso? Escribe a hello@ikiform.com.