Formulaire de contact sur un site statique7 min de lecture
Ajouter un formulaire de contact sur Netlify sans Netlify Forms
Un formulaire de contact sur Netlify sans Netlify Forms : pas de détection au build, envois JSON depuis React, et un formulaire à part pour les deploy previews.
Laissez data-netlify hors du formulaire et faites pointer son action vers votre endpoint Ikiform ; Netlify sert alors la page sans jamais toucher à l'envoi. Les formulaires React et Vue n'ont besoin d'aucune copie HTML masquée, et peuvent envoyer du JSON. Définissez un id de formulaire différent pour les deploy previews dans netlify.toml, car les hôtes de préproduction ne correspondent pas à votre site dans Allowed domains.
Ce qui change sur Netlify
Netlify est le seul grand hébergeur statique avec une fonctionnalité de formulaires intégrée. Netlify Forms lit votre HTML déployé après chaque build, trouve les formulaires marqués data-netlify ou netlify, et accepte les envois vers votre propre site pour ces formulaires. Tout autre envoi vers une page statique échoue, et sur Netlify il échoue avec 404, pas 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: 0Ce 404 pousse à chercher une page manquante qui ne l'est pas. Il signifie qu'aucun formulaire détecté ne correspondait à l'envoi. Savoir s'il faut utiliser Netlify Forms est une autre question, et le comparatif Ikiform vs Netlify Forms y répond honnêtement : si votre site restera toujours sur Netlify et que le formulaire est en HTML simple, Netlify Forms demande moins de travail. Cette page couvre les autres cas.
| Votre situation | Pourquoi Netlify Forms gêne |
|---|---|
| Le formulaire est rendu par React, Vue ou Svelte | Le build analyse le HTML statique : un formulaire rendu côté client a donc besoin d'un double masqué que Netlify puisse trouver |
Vous voulez envoyer du JSON depuis fetch | Netlify Forms n'accepte que des corps urlencoded et multipart |
| Le site pourrait changer d'hébergeur | Le formulaire envoie vers votre propre domaine : il ne fonctionne donc que tant que Netlify sert ce domaine |
| Les messages doivent arriver à un webhook que vous vérifiez | Ikiform signe chaque livraison avec les en-têtes Standard Webhooks et la réessaie |
- Avec Netlify Forms, Netlify analyse le HTML de chaque déploiement à la recherche des formulaires marqués data-netlify.
- Un formulaire trouvé accepte les envois vers votre propre site, et Netlify les stocke.
- Un formulaire non trouvé, comme un formulaire rendu en JavaScript sans copie masquée, reçoit 404 à chaque envoi.
- Avec Ikiform, rien n'a besoin d'être trouvé au build.
- Le formulaire envoie directement à ikiform.com, en HTML ou en JSON, et Ikiform le stocke et vous l'envoie par e-mail.
Sortir un formulaire de Netlify Forms
- Retirez
data-netlifyounetlify,netlify-honeypotetdata-netlify-recaptchadu formulaire, ainsi que tout input masquéform-name. Si vous le laissez,form-nameest stocké comme un champ ordinaire. - Supprimez la copie statique masquée d'un formulaire JavaScript, comme celle ci-dessous. Netlify n'en avait besoin que pour détecter le formulaire.
- Réglez l'
action, ou l'URL dufetch, sur votre endpoint Ikiform. L'ancienaction="/thanks"passe dans le réglage Redirect after submit du formulaire. - Téléchargez d'abord les soumissions passées depuis la page Forms de Netlify. Elles ne migrent pas toutes seules.
<form name="contact" netlify hidden>
<input name="name" />
<input name="email" />
<textarea name="message"></textarea>
</form>Laissez la détection des formulaires activée si d'autres formulaires du site utilisent encore Netlify Forms. La documentation de Netlify indique que la désactivation n'est prévue que pour les sites qui n'utilisent pas Netlify Forms. Une fois que plus rien ne l'utilise, la désactiver évite une analyse à chaque déploiement.
Ce qui remplace les réglages anti-spam de Netlify
Netlify passe les soumissions dans Akismet et propose netlify-honeypot et reCAPTCHA. Sur Ikiform, le honeypot continue de fonctionner si vous réglez le Honeypot field du formulaire sur le nom que vous utilisez déjà, comme bot-field. Le score de spam s'applique à chaque envoi sans configuration, et Spam check réglé sur Ikiform remplace reCAPTCHA par Cloudflare Turnstile, sans clés. Retirez la div reCAPTCHA lors de la bascule.
Un formulaire React qui envoie du JSON
Sans Netlify Forms dans les pattes, un formulaire rendu côté client n'a pas besoin de double, et il peut envoyer du JSON, ce qui préserve les types et permet de nettoyer les valeurs au préalable. Celui-ci enregistre aussi quel contexte Netlify a construit la page :
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/jsonindique à Ikiform d'analyser du JSON ;Accept: application/jsondemande une réponse en JSON.VITE_CONTEXTest un nom d'exemple. Netlify définitCONTEXTpendant le build ; exposez-le à Vite dans votre commande de build, ou supprimez le champ.- Le honeypot est envoyé comme clé JSON. Gardez-le dans le corps même si l'input est masqué, pour que les bots qui le remplissent se signalent toujours.
Les deploy previews ont leur propre formulaire
Les deploy previews vivent sur des hôtes comme deploy-preview-12--your-site.netlify.app. Ce n'est pas un sous-domaine de your-site.netlify.app, il ne correspond donc pas dans Allowed domains, et les envois des préproductions finissent en spam. Donnez aux previews et aux branch deploys leur propre formulaire dans netlify.toml, et définissez l'en-tête Content-Security-Policy dans le même fichier :
[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"Laissez vides les Allowed domains du formulaire de préproduction, ou ajoutez l'hôte de préproduction exact pendant vos tests. Le formulaire de production reçoit votre domaine et your-site.netlify.app. Les en-têtes de netlify.toml s'appliquent à tous les contextes de déploiement ; si vous préférez un fichier _headers, il doit se retrouver dans le répertoire de publication.
netlify.app seul dans Allowed domains. Tous les sites Netlify correspondraient, y compris des copies de votre formulaire sur les sites d'autres personnes.Vérifiez que tout fonctionne
- Ouvrez une deploy preview, envoyez, et vérifiez que le message arrive dans le formulaire de préproduction.
- Fusionnez, puis envoyez en production. Vérifiez que le message arrive dans le formulaire de production et que la page Forms de Netlify n'affiche rien de nouveau.
- Vérifiez la boîte de réception de chaque destinataire des notifications. Chacun reçoit un e-mail avec les champs, et Reply répond à l'expéditeur quand le formulaire a un champ
email. - Envoyez avec le honeypot rempli,
_gotcha=bot. L'envoi reçoit quand même une réponse de succès, mais la soumission est stockée comme spam et n'envoie ni e-mail ni webhook. - Rien n'est arrivé ? Passez le filtre de statut du tableau de bord sur Spam. Le spam reçoit la même réponse de succès qu'une vraie soumission.
Pièges
| Problème | Que faire |
|---|---|
404 à l'envoi | L'envoi est parti vers votre propre site. Un build en cache a peut-être encore l'ancienne action, ou un appel fetch envoie toujours vers /. Cherchez form-name dans le répertoire de publication. |
| Les soumissions apparaissent toujours dans Netlify | Un formulaire de la page envoie encore vers votre propre site avec data-netlify ou netlify : Netlify continue donc de le détecter et de stocker ses envois. Retirez l'attribut, réglez l'action sur Ikiform et redéployez. |
404 not_found d'Ikiform sur les préproductions uniquement | Le contexte deploy preview n'a pas de VITE_IKIFORM_FORM_ID, l'URL se termine donc par undefined. Ajoutez-le sous [context.deploy-preview.environment]. |
403 origin_not_allowed, ou tous les envois finissent en spam | Le domaine de la page n'est pas dans les Allowed domains du formulaire. Ajoutez-le, plus localhost pour les tests en local. Les sous-domaines d'un domaine listé sont autorisés. |
422 files_not_supported | Le formulaire a un <input type="file"> avec un fichier choisi. L'envoi de fichiers n'est pas encore pris en charge ; retirez l'input ou demandez plutôt un lien. |
Questions
Oui. Netlify ne gère que les formulaires qu'il a détectés au build. Un formulaire sans data-netlify dont l'action pointe vers un autre endpoint est servi par Netlify comme n'importe quelle page et envoie directement vers cet endpoint.
Netlify répond 404 à un POST vers une page statique, sauf si Netlify Forms a détecté ce formulaire dans le HTML déployé. Marquez le formulaire pour Netlify Forms, ou faites pointer son action vers un autre backend.
Oui. La détection se fait par formulaire, via l'attribut. Gardez data-netlify sur les formulaires que Netlify doit gérer et laissez la détection activée.
Elles restent dans Netlify, pas dans Ikiform. Téléchargez-les depuis la page Forms du site avant de désactiver la détection, et gardez le fichier dans vos archives.
Pas encore. Un envoi contenant un fichier est refusé avec 422 files_not_supported. Demandez plutôt un lien vers le fichier, comme une URL de drive partagé.
Une étape vous bloque, ou vous voulez un guide pour votre cas ? Écrivez à hello@ikiform.com.