Home / Guide / Integrazione del modulo di contatto React (app Vite e a pagina singola)
GUIDA ALL'INTEGRAZIONE · REACT & SPA

Integrazione del modulo di contatto React (app Vite e a pagina singola)

Come aggiungere moduli di contatto e feedback alle applicazioni React Single-Page (SPA) create con Vite o Create React App, con difesa dallo spam honeypot e notifiche istantanee della posta in arrivo.

1. Componente del modulo di contatto React standard con Vite

Questo componente React leggero utilizza hook React standard (useState) e l'API Fetch del browser per inviare direttamente al tuo endpoint pubblico Lugatu. Non sono necessarie librerie di moduli esterni o server backend.

src/components/ContactForm.tsx tsx
import React, { useState } from 'react';

export function ContactForm() {
  const [formData, setFormData] = useState({ name: '', email: '', message: '', _gotcha: '' });
  const [status, setStatus] = useState<'idle' | 'submitting' | 'success' | 'error'>('idle');
  const [errorMessage, setErrorMessage] = useState('');

  const handleSubmit = async (e: React.FormEvent) => {
    e.preventDefault();
    if (formData._gotcha) return; // Silent discard for automated bot traps

    setStatus('submitting');
    setErrorMessage('');

    try {
      const response = await fetch('https://www.lugatu.com/api/v1/public/forms/YOUR_PUBLIC_FORM_ID/submissions', {
        method: 'POST',
        headers: {
          'Content-Type': 'application/json',
          'Accept': 'application/json',
        },
        body: JSON.stringify({
          fields: {
            name: formData.name,
            email: formData.email,
            message: formData.message,
          },
          honeypot: formData._gotcha,
        }),
      });

      if (!response.ok) {
        throw new Error('Failed to deliver message');
      }

      setStatus('success');
      setFormData({ name: '', email: '', message: '', _gotcha: '' });
    } catch (err: any) {
      setStatus('error');
      setErrorMessage(err.message || 'Submission error. Please try again.');
    }
  };

  return (
    <form onSubmit={handleSubmit} className="space-y-4 max-w-md">
      <div>
        <label htmlFor="react-name" className="block text-sm font-semibold mb-1">Your Name</label>
        <input
          id="react-name"
          type="text"
          required
          value={formData.name}
          onChange={(e) => setFormData({ ...formData, name: e.target.value })}
          className="w-full border rounded-xl p-2.5"
        />
      </div>

      <div>
        <label htmlFor="react-email" className="block text-sm font-semibold mb-1">Email Address</label>
        <input
          id="react-email"
          type="email"
          required
          value={formData.email}
          onChange={(e) => setFormData({ ...formData, email: e.target.value })}
          className="w-full border rounded-xl p-2.5"
        />
      </div>

      <div>
        <label htmlFor="react-message" className="block text-sm font-semibold mb-1">Message</label>
        <textarea
          id="react-message"
          rows={4}
          required
          value={formData.message}
          onChange={(e) => setFormData({ ...formData, message: e.target.value })}
          className="w-full border rounded-xl p-2.5"
        />
      </div>

      <!-- Honeypot spam defense -->
      <input
        type="text"
        name="_gotcha"
        value={formData._gotcha}
        onChange={(e) => setFormData({ ...formData, _gotcha: e.target.value })}
        style={{ display: 'none' }}
        tabIndex={-1}
        autoComplete="off"
      />

      <button
        type="submit"
        disabled={status === 'submitting'}
        className="btn-primary-editorial py-2.5 px-6"
      >
        {status === 'submitting' ? 'Sending...' : 'Send Message'}
      </button>

      {status === 'success' && (
        <p className="text-sm text-green-700 font-medium">Thank you! Your message was delivered.</p>
      )}
      {status === 'error' && (
        <p className="text-sm text-red-600">{errorMessage}</p>
      )}
    </form>
  );
}

2. Integrazione con React Hook Form e Formik

Il modello di invio JSON standard di Lugatu si integra facilmente con le librerie di convalida dei moduli come React Hook Form, Formik o Zod. Passa i valori serializzati direttamente nell'oggetto payload dei campi.

useLugatuForm.ts typescript
// React Hook Form submission handler
const onSubmit = async (data: FormValues) => {
  const res = await fetch(`https://www.lugatu.com/api/v1/public/forms/${import.meta.env.VITE_LUGATU_FORM_ID}/submissions`, {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ fields: data }),
  });
  return res.ok;
};

3. Variabili d'ambiente e separazione segreta in React

In Vite, le variabili pubbliche hanno il prefisso VITE_. Utilizza VITE_LUGATU_FORM_ID per l'ID del modulo pubblico. Non esporre mai la chiave API server-to-server (lug_live_...) all'interno del codice sorgente o dei repository lato client.

warning

Le chiavi API live che iniziano con lug_live_ sono strettamente riservate all'esecuzione da server a server. Qualsiasi richiesta del browser inviata con una chiave API attiva viene rifiutata con HTTP 403.

Invariante di sicurezza e autenticazione

Invariante di sicurezza: non anteporre mai alle chiavi API live VITE_ o REACT_APP_. I client browser devono inviare esclusivamente a endpoint pubblici senza chiavi API.

Risoluzione dei problemi ed errori comuni di importazione

Gli invii di moduli restituiscono un errore CORS nella console del browser

Assicurati di inviare all'endpoint pubblico (https://www.lugatu.com/api/v1/public/forms/YOUR_PUBLIC_FORM_ID/submissions). Gli endpoint da server a server (POST /api/v1/forms/{id}/submissions) bloccano esplicitamente le origini del browser.

Domande frequenti

Posso usare React Hook Form o Formik con Lugatu?

SÌ. Lugatu accetta JSON standard con un oggetto campi. Qualsiasi libreria di moduli React può serializzare i propri valori e inviarli all'endpoint pubblico.

Pronto per creare l'endpoint del modulo?

Inizia gratuitamente in pochi secondi. Nessuna carta di credito richiesta.

Inizia gratis →