Configuration

Formulaire de contact

Onyx inclut un formulaire de contact complet avec validation côté client et envoi d'email via Resend. Deux variables d'environnement suffisent pour l'activer.

Comment ça fonctionne

Quand l'utilisateur soumet le formulaire, voici ce qui se passe :

  • Validation clientreact-hook-form + zod valident les champs avant envoi
  • API RouteLa requête POST est envoyée à /api/contact
  • ResendLe serveur appelle l'API Resend pour envoyer l'email
  • ToastUn message de confirmation ou d'erreur apparaît via sonner

Variables d'environnement

Créez un fichier .env.local à la racine du projet et renseignez ces deux variables :

.env.local
# Clé API obtenue sur resend.com
RESEND_API_KEY=re_xxxxxxxxxxxxxxxxxx

# Adresse qui recevra les messages
CONTACT_EMAIL=vous@votredomaine.fr
Pour obtenir une clé API Resend gratuite (jusqu'à 3 000 emails/mois), créez un compte sur resend.com et vérifiez votre domaine d'envoi.

Ajouter le formulaire dans une page

Le bloc ContactFormSection est disponible dans src/components/blocks/contact-form-section.tsx. Importez-le dans n'importe quelle page :

src/app/contact/page.tsx
import { ContactFormSection } from "@/components/blocks/contact-form-section";

export default function ContactPage() {
  return <ContactFormSection />;
}

Champs du formulaire

Par défaut, le formulaire comporte trois champs. Pour les modifier, éditez directement src/components/blocks/contact-form-section.tsx.

ChampTypeValidation
nametextRequis, 2 caractères minimum
emailemailRequis, format email valide
messagetextareaRequis, 10 caractères minimum

Ajouter un champ

Pour ajouter un champ (ex. : numéro de téléphone), étendez le schéma Zod et le formulaire dans le même fichier :

contact-form-section.tsx
const contactSchema = z.object({
  name: z.string().min(2),
  email: z.string().email(),
  phone: z.string().optional(),  // nouveau champ
  message: z.string().min(10),
});
N'oubliez pas d'inclure le nouveau champ dans le corps de la requête envoyée à l'API Route src/app/api/contact/route.ts, et de l'ajouter au template HTML de l'email.

Route API

La logique serveur est dans src/app/api/contact/route.ts. Elle reçoit le POST, valide les données, et appelle Resend :

src/app/api/contact/route.ts (simplifié)
import { Resend } from "resend";

const resend = new Resend(process.env.RESEND_API_KEY);

export async function POST(req: Request) {
  const { name, email, message } = await req.json();
  await resend.emails.send({
    from: "contact@votredomaine.fr",
    to: process.env.CONTACT_EMAIL,
    subject: `Message de ${name}`,
    html: `<p>${message}</p>`,
  });
}

Protection anti-spam

Onyx inclut un champ honeypot invisible par défaut. Un humain ne le voit pas et ne le remplit jamais. Un bot parcourt le DOM et le remplit automatiquement — la soumission est alors silencieusement ignorée côté serveur, sans révéler la détection.

Pour la grande majorité des sites vitrines, ce mécanisme suffit : il bloque 99 % des spambots génériques sans aucune clé API ni friction pour l'utilisateur.

Aller plus loin avec un CAPTCHA

Si votre site reçoit un volume important de soumissions ou devient une cible connue, ajoutez un CAPTCHA. Les trois options les plus courantes :

SolutionUXPrixNotes
Cloudflare TurnstileInvisibleGratuitRecommandé — zéro friction, RGPD-friendly, pas de cookies
hCaptchaChallenge visuelGratuit (limité)Open source, privacy-first, alternative à reCAPTCHA
Google reCAPTCHA v3Invisible (score)GratuitScore de confiance 0-1, pas de challenge visible
Pour intégrer un CAPTCHA, demandez à Claude ou Cursor : “Ajoute Cloudflare Turnstile au formulaire de contact. La clé secrète doit être dans TURNSTILE_SECRET_KEY dans .env.local et vérifiée côté serveur dans la route API avant d'envoyer l'email.” L'IA génère l'intégration complète en quelques secondes — pensez à préciser que la clé ne doit jamais être exposée côté client.

Tester en développement

Sans RESEND_API_KEY, la route API retourne une erreur 500. Pour tester l'interface sans envoyer d'email, deux options :

  • Resend sandboxEn mode test (clé préfixée re_test_), les emails sont interceptés dans le dashboard Resend sans être livrés
  • Console logCommentez l'appel Resend et remplacez-le par un console.log() pour tester la validation et l'UI