React iletişim formu: backend olmadan e-posta

Formgong team ·

React iletişim formundan mail göndermek için FormData'yı fetch ile Formgong adresine yollayın ve herkese açık erişim anahtarını yapıştırın. Formgong spamı süzer, başvuruyu e-postaya ve Telegram'a iletir. Telegram anında gelir. Ücretsiz planda e-posta günlük özettir. Kendi sunucunuzu, SMTP'yi veya gizli anahtarı çalıştırmazsınız. Aşağıdaki hazır bileşeni yapıştırın.

Bir form oluşturun, erişim anahtarını kopyalayın ve bileşendeki fk_your_access_key değerini değiştirin. Anahtar herkese açıktır. Yeri bu dosyadır.

Vite veya Create React App
import { useState } from "react";

// Anahtarınızı yapıştırın: fk_your_access_key değerini Formgong formunun anahtarıyla değiştirin.
const ACCESS_KEY = "fk_your_access_key";

export default function ContactForm() {
  const [status, setStatus] = useState("idle");
  const [notice, setNotice] = useState("");

  async function onSubmit(event) {
    event.preventDefault();
    const form = event.currentTarget;
    if (!(form instanceof HTMLFormElement) || !form.reportValidity()) return;
    const body = new FormData(form);
    body.set("access_key", ACCESS_KEY);
    body.set("_lang", "tr");
    setStatus("sending");
    setNotice("");
    try {
      const response = await fetch("https://formgong.com/submit", {
        method: "POST",
        headers: { Accept: "application/json" },
        body,
      });
      const result = await response.json();
      if (!response.ok || !result.success) {
        setStatus("error");
        setNotice(result.message || "Gönderilemedi. Lütfen tekrar deneyin.");
        return;
      }
      form.reset();
      setStatus("success");
      setNotice(result.message || "Gönderildi. Teşekkürler!");
    } catch {
      setStatus("error");
      setNotice("Bağlantı kurulamadı. Lütfen tekrar deneyin.");
    }
  }

  const sending = status === "sending";
  return (
    <form onSubmit={onSubmit}>
      <label>{"Ad"} <input name="name" autoComplete="name" placeholder={"Adınız"} required /></label>
      <label>{"E-posta"} <input name="email" type="email" autoComplete="email" placeholder={"siz@ornek.com"} required /></label>
      <label>{"Mesaj"} <textarea name="message" placeholder={"Size nasıl yardımcı olabiliriz?"} required /></label>
      <div aria-hidden="true" style={{ position: "absolute", insetInlineStart: 0, top: 0, width: 1, height: 1, overflow: "hidden", clipPath: "inset(50%)" }}>
        <input name="botcheck" tabIndex={-1} autoComplete="off" />
      </div>
      <button type="submit" disabled={sending}>{sending ? "Gönderiliyor…" : "Gönder"}</button>
      <p role={status === "error" ? "alert" : "status"}>{notice}</p>
    </form>
  );
}

Diğer React kurulumları

Vite veya Create React App

Yukarıdaki bileşen bu kurulumdur. src/ContactForm.jsx olarak kaydedin ve App içinden gösterin. Ek paket yok.

Vite veya Create React App
// src/App.jsx — Vite or Create React App
import ContactForm from "./ContactForm";

export default function App() {
  return <ContactForm />;
}
Next.js App Router

Dosyanın başına istemci direktifini ekleyin. Formu gösteren sayfa Server Component olarak kalabilir.

Next.js App Router
"use client";

import { useState } from "react";

// Anahtarınızı yapıştırın: fk_your_access_key değerini Formgong formunun anahtarıyla değiştirin.
const ACCESS_KEY = "fk_your_access_key";

export default function ContactForm() {
  const [status, setStatus] = useState("idle");
  const [notice, setNotice] = useState("");

  async function onSubmit(event) {
    event.preventDefault();
    const form = event.currentTarget;
    if (!(form instanceof HTMLFormElement) || !form.reportValidity()) return;
    const body = new FormData(form);
    body.set("access_key", ACCESS_KEY);
    body.set("_lang", "tr");
    setStatus("sending");
    setNotice("");
    try {
      const response = await fetch("https://formgong.com/submit", {
        method: "POST",
        headers: { Accept: "application/json" },
        body,
      });
      const result = await response.json();
      if (!response.ok || !result.success) {
        setStatus("error");
        setNotice(result.message || "Gönderilemedi. Lütfen tekrar deneyin.");
        return;
      }
      form.reset();
      setStatus("success");
      setNotice(result.message || "Gönderildi. Teşekkürler!");
    } catch {
      setStatus("error");
      setNotice("Bağlantı kurulamadı. Lütfen tekrar deneyin.");
    }
  }

  const sending = status === "sending";
  return (
    <form onSubmit={onSubmit}>
      <label>{"Ad"} <input name="name" autoComplete="name" placeholder={"Adınız"} required /></label>
      <label>{"E-posta"} <input name="email" type="email" autoComplete="email" placeholder={"siz@ornek.com"} required /></label>
      <label>{"Mesaj"} <textarea name="message" placeholder={"Size nasıl yardımcı olabiliriz?"} required /></label>
      <div aria-hidden="true" style={{ position: "absolute", insetInlineStart: 0, top: 0, width: 1, height: 1, overflow: "hidden", clipPath: "inset(50%)" }}>
        <input name="botcheck" tabIndex={-1} autoComplete="off" />
      </div>
      <button type="submit" disabled={sending}>{sending ? "Gönderiliyor…" : "Gönder"}</button>
      <p role={status === "error" ? "alert" : "status"}>{notice}</p>
    </form>
  );
}

// app/contact/page.jsx — this file can stay a Server Component.
import ContactForm from "./ContactForm";

export default function Page() {
  return <ContactForm />;
}
React Hook Form

react-hook-form kurun. Doğrulamayı onda bırakın. Gönderim işleyicisi yine FormData'yı Formgong'a postalar.

React Hook Form
import { useState } from "react";
import { useForm } from "react-hook-form";

// Anahtarınızı yapıştırın: fk_your_access_key değerini Formgong formunun anahtarıyla değiştirin.
const ACCESS_KEY = "fk_your_access_key";

export default function ContactForm() {
  const { register, handleSubmit, reset, formState: { errors, isSubmitting } } = useForm();
  const [notice, setNotice] = useState("");
  const [failed, setFailed] = useState(false);

  async function onSubmit(values) {
    const body = new FormData();
    body.set("access_key", ACCESS_KEY);
    body.set("_lang", "tr");
    body.set("name", values.name);
    body.set("email", values.email);
    body.set("message", values.message);
    body.set("botcheck", values.botcheck || "");
    setFailed(false);
    setNotice("");
    try {
      const response = await fetch("https://formgong.com/submit", {
        method: "POST",
        headers: { Accept: "application/json" },
        body,
      });
      const result = await response.json();
      if (!response.ok || !result.success) {
        setFailed(true);
        setNotice(result.message || "Gönderilemedi. Lütfen tekrar deneyin.");
        return;
      }
      reset();
      setNotice(result.message || "Gönderildi. Teşekkürler!");
    } catch {
      setFailed(true);
      setNotice("Bağlantı kurulamadı. Lütfen tekrar deneyin.");
    }
  }

  return (
    <form onSubmit={handleSubmit(onSubmit)}>
      <label>{"Ad" <input {...register("name", { required: "Zorunlu" })} aria-invalid={errors.name ? true : undefined} autoComplete="name" /></label>
      {errors.name && <span>{errors.name.message}</span>}
      <label>{"E-posta" <input type="email" {...register("email", { required: "Zorunlu" })} aria-invalid={errors.email ? true : undefined} autoComplete="email" /></label>
      {errors.email && <span>{errors.email.message}</span>}
      <label>{"Mesaj" <textarea {...register("message", { required: "Zorunlu" })} aria-invalid={errors.message ? true : undefined} /></label>
      {errors.message && <span>{errors.message.message}</span>}
      <div aria-hidden="true" style={{ position: "absolute", insetInlineStart: 0, top: 0, width: 1, height: 1, overflow: "hidden", clipPath: "inset(50%)" }}>
        <input {...register("botcheck")} tabIndex={-1} autoComplete="off" />
      </div>
      <button type="submit" disabled={isSubmitting}>{isSubmitting ? "Gönderiliyor…" : "Gönder"}</button>
      {failed ? <p role="alert">{notice}</p> : <p role="status">{notice}</p>}
    </form>
  );
}
TypeScript

Aynı bileşen, durum ve JSON sonucu için tiplerle. ContactForm.tsx olarak kaydedin.

TypeScript
import { useState, type FormEvent } from "react";

// Anahtarınızı yapıştırın: fk_your_access_key değerini Formgong formunun anahtarıyla değiştirin.
const ACCESS_KEY = "fk_your_access_key";

type Status = "idle" | "sending" | "success" | "error";
type SubmitResult = { success?: boolean; message?: string };

export default function ContactForm() {
  const [status, setStatus] = useState<Status>("idle");
  const [notice, setNotice] = useState("");

  async function onSubmit(event: FormEvent<HTMLFormElement>) {
    event.preventDefault();
    const form = event.currentTarget;
    if (!form.reportValidity()) return;
    const body = new FormData(form);
    body.set("access_key", ACCESS_KEY);
    body.set("_lang", "tr");
    setStatus("sending");
    setNotice("");
    try {
      const response = await fetch("https://formgong.com/submit", {
        method: "POST",
        headers: { Accept: "application/json" },
        body,
      });
      const result = (await response.json()) as SubmitResult;
      if (!response.ok || !result.success) {
        setStatus("error");
        setNotice(result.message || "Gönderilemedi. Lütfen tekrar deneyin.");
        return;
      }
      form.reset();
      setStatus("success");
      setNotice(result.message || "Gönderildi. Teşekkürler!");
    } catch {
      setStatus("error");
      setNotice("Bağlantı kurulamadı. Lütfen tekrar deneyin.");
    }
  }

  const sending = status === "sending";
  return (
    <form onSubmit={onSubmit}>
      <label>{"Ad"} <input name="name" autoComplete="name" placeholder={"Adınız"} required /></label>
      <label>{"E-posta"} <input name="email" type="email" autoComplete="email" placeholder={"siz@ornek.com"} required /></label>
      <label>{"Mesaj"} <textarea name="message" placeholder={"Size nasıl yardımcı olabiliriz?"} required /></label>
      <div aria-hidden="true" style={{ position: "absolute", insetInlineStart: 0, top: 0, width: 1, height: 1, overflow: "hidden", clipPath: "inset(50%)" }}>
        <input name="botcheck" tabIndex={-1} autoComplete="off" />
      </div>
      <button type="submit" disabled={sending}>{sending ? "Gönderiliyor…" : "Gönder"}</button>
      <p role={status === "error" ? "alert" : "status"}>{notice}</p>
    </form>
  );
}

GitHub'da hazır React bileşeni: github.com/formgong/react-contact-form. MIT lisansı: “Use this template” düğmesine tıklayın veya dosyaları kopyalayın.

React paketi: npm install @formgong/react (npm: @formgong/react)

Mevcut projeye ekleyin: npx formgong init (npm: formgong)

React ile formlar nasıl çalışır

React formu çizer. Postayı göndermez. Vite ve Create React App statik dosya verir, bu yüzden tarayıcının POST'u kabul eden bir adrese ihtiyacı vardır.

Bu adres Formgong'dur. Bileşen FormData verisini fetch ile /submit adresine yollar. Yanında herkese açık access_key, _lang ve boş botcheck alanı gider. Formgong spamı süzer ve başvuruyu iletir.

Burada ne farklı

  • Telegram ücretsiz planda bile anında gelir. E-posta da vardır: tek alıcı için günlük özet.
  • Başvurular AB'de, AB yargı yetkisindeki Cloudflare D1 üzerinde saklanır.
  • Spam koruması CAPTCHA veya çerez istemez. Honeypot her zaman açıktır. Turnstile isteğe bağlıdır.
  • Bileşendeki erişim anahtarı herkese açıktır. SMTP parolasını veya özel bir API anahtarını paketlemezsiniz.
  • Hata metni ve teşekkür sayfası ziyaretçinin dilini izleyebilir. Dil sayısı 12'dir.

Neden EmailJS ya da kendi API rotası değil?

Neden EmailJS ya da kendi API rotası değil?
EmailJSKendi API rotanızFormgong
Ne eklersinizBir npm paketi, service id, template id ve herkese açık anahtar.Bir serverless fonksiyon ve Nodemailer gibi bir SMTP istemcisi.Bu sayfadaki bileşende tek bir herkese açık erişim anahtarı.
E-postaKendi şablonunuzdan anında posta. Ücretsiz plan ayda 200 istek ve 2 şablondur.Sağlayıcınız iletiyi kabul ederse anında posta.Ücretsiz planda günlük özet. Her başvuru Pro ve Business'ta e-posta olur.
TelegramEmailJS'in parçası değil. Servis e-posta gönderir.Yalnızca bu çağrıyı kendiniz yazarsanız.Ücretsiz planda anında teslim.
Tarayıcıdaki anahtarlarHerkese açık anahtar açık kalmalıdır. Özel anahtarı sunucuda tutun.SMTP parolası host ortamında kalır. Bu yolun güçlü yanı budur.Erişim anahtarı herkese açıktır. SMTP sırrı yoktur.
SpamLimitleri EmailJS panelinden siz koyarsınız. 2024 Webtips rehberi hiçbir şey eklemez.Filtreyi siz yazarsınız.Honeypot her zaman açıktır. Çerez yok. CAPTCHA gerekmez.
Ücretsiz kotaAyda 200 istek, sonrası fazlalık düşer.Hostunuzun ücretsiz katmanı ve posta sağlayıcınızın limiti.Ayda 300 başvuru.
Veri nerede dururEmailJS geçmişi kendi sunucularında tutar. Ücretsiz plan 7 gündür. Fiyat sayfası yalnızca AB demez.Hostunuzun ve posta sağlayıcınızın tuttuğu yerde.AB'de, Cloudflare D1 üzerinde.

Posta kendi kutunuzdan, kendi şablonunuzla, gönderildiği anda çıksın istiyorsanız EmailJS seçin.

Gönderim kendi veritabanınızı güncelleyecek ya da ziyaretçinin giriş yaptığını denetleyecekse Vercel veya Netlify fonksiyonu seçin.

İletişim formu için Formgong seçin. Ücretsiz planda Telegram anlıktır. O planda e-posta günlük özettir. SMTP parolası saklamazsınız.

EmailJS rakamları 6 Ekim 2026 fiyat sayfasındandır. API rotası sütunu, bir firmanın fiyat listesi değil, Vercel veya Netlify fonksiyonu artı SMTP'dir.

React ile kurulum

  1. Formgong'da bir form oluşturun ve erişim anahtarını kopyalayın. fk_your_access_key gibi görünür.
  2. Yukarıdaki bileşeni uygulamaya yapıştırın ve bu anahtarı değiştirin.
  3. Bileşeni gösterin, siteyi açın ve kendinize bir deneme gönderin.
  4. Formgong'da formu açın ve telefona uyarı istiyorsanız Telegram'ı bağlayın.

Sorular ve yanıtlar

React iletişim formu için backend gerekir mi?

Hayır. FormData verisini fetch ile Formgong'a gönderin. API rotası, Nodemailer veya SMTP eklemezsiniz.

Nodemailer veya SMTP olmadan React'ten e-posta nasıl gönderilir?

Bu sayfadaki bileşeni kullanın. Formgong POST'u kabul eder ve spamı süzer. Telegram'a başvuru anında gider. Ücretsiz planda e-posta günlük özettir. Pro ve Business her başvuru için e-posta yollar.

Anahtarı istemci koduna koymak güvenli mi?

Evet. access_key herkese açıktır. Yalnızca bu forma gönderim yapar. Gelen kutusunu okuyamaz. Pro'da izinli alan adlarını sınırlayabilirsiniz. SMTP parolasını pakete koymayın.

React formunda spam nasıl durdurulur?

botcheck alanını boş bırakın. Formgong spamı çerez olmadan ve zorunlu CAPTCHA olmadan puanlar. İleride bir bulmaca isterseniz Turnstile isteğe bağlıdır.

Bu Next.js ile çalışır mı?

Evet. App Router'da dosyanın başına "use client" ekleyin. Bileşen aynı kalır. Bu sayfadaki Next.js varyantı dosyayı gösterir.

Ücretsiz başla