ฟอร์มติดต่อมีประโยชน์บนเว็บไซต์หากคุณต้องการให้ผู้ใช้โต้ตอบกับคุณผ่านอีเมล และมีหลายวิธีในการสร้าง
แบบดั้งเดิมคุณอาจใช้ PHP ส่งอีเมลผ่านเซิร์ฟเวอร์ หรือบริการบุคคลที่สามที่จัดการตรรกะอีเมล
แต่ในบทความนี้ เราจะพูดถึงวิธีส่งอีเมลจากแอปพลิเคชัน Next.js ด้วย SendGrid API
เราจะสร้างหน้าเว็บง่ายๆ – ฟอร์มติดต่อที่สร้างด้วย React – ที่มีฟิลด์ป้อนข้อมูลซึ่งเราจะตรวจสอบก่อนส่ง เราจะเชื่อมฟอร์มกับ SendGrid API ซึ่งจะดูแลการส่งอีเมลให้คุณ แล้วในตอนท้ายของวัน ทั้งที่คุณต้องทำคือเช็คอีเมลเพื่อหาคำถามเหล่านั้น
อย่างไรก็ตาม หากคุณยังไม่มีโปรเจกต์ Next.js คุณสามารถสร้างได้ง่ายและผสานกับ Vercel โดยทำตามขั้นตอนด้านล่าง:
- สร้างบัญชีบน Vercel และคลิก
New Project

2. เลือกเทมเพลตเป็น Next.js:

3. ตั้งชื่อ repository อะไรก็ได้และคลิก create project (เลือก GitHub, GitLab หรือ BitBucket สำหรับ version control)

ตามขั้นตอนข้างต้นสามข้อ คุณจะมี repository บนบัญชี version control ของคุณ
Tech Stack ที่เราจะใช้
- Next.js สำหรับสร้างหน้า landing page ฟอร์มติดต่อ
- TailwindCSS สำหรับจัดสไตล์คอมโพเนนต์
- SendGrid สำหรับส่งอีเมลผ่าน API ของพวกเขา
- Vercel สำหรับโฮสต์แอปพลิเคชันและ CI/CD
เราจะใช้ API routes ของ Next.js เพื่อจัดการเหตุการณ์ฟอร์ม โมดูล API ให้วิธีที่ยืดหยุ่นในการจัดการตรรกะแบ็กเอนด์ในแอป Next.js ของเรา
โค้ดที่เราเขียนในโฟลเดอร์ API จะถูก deploy เป็น Serverless function บน Vercel สำหรับโฮสติ้ง อ่านเพิ่มเติมเกี่ยวกับ Next.js API routes ได้ที่นี่
หากคุณมีโปรเจกต์ Next.js อยู่แล้วที่ต้องการตั้งค่าฟอร์มติดต่อที่ทำงานได้ นั่นดีมาก ในกรณีนั้น จะง่ายสำหรับคุณที่จะสร้างหน้าและเริ่มทันที
แต่ถ้ายังไม่มีโปรเจกต์ก็ไม่เป็นไร – ไปที่ Vercel และสร้าง Next.js starter project แล้ว clone repository
Application Flow
มาดู application flow – หรือว่าการส่งอีเมลทำงานอย่างไร:
- ผู้ใช้ปลายทางกรอก 4 ฟิลด์บังคับ และคลิก submit
- เมื่อ submit ฟังก์ชัน
handleSubmitจะถูกทริกเกอร์ handleSubmitตรวจสอบฟอร์มสำหรับฟิลด์ป้อนข้อมูล และเช็คว่างไม่ได้- ถ้าฟิลด์ไม่ว่าง จะมี API call ไปที่
api/sendgridซึ่งตรรกะการส่งอีเมลอยู่ที่นั่น - ใน
api/sendgridโมดูล@sendgrid/mailเริ่มต้นฟังก์ชันsendที่รับ API keys ของแอปพลิเคชันและส่งอีเมลพร้อมฟิลด์ที่จำเป็น - หากส่งอีเมลสำเร็จ จะส่ง response
200กลับไปที่ client ไม่เช่นนั้น400 - Response ถูกจัดการที่ frontend และข้อความที่เหมาะสมจะถูกแสดง
วิธีตั้งค่า TailwindCSS
การตั้งค่า TailwindCSS ค่อนข้างง่าย และทำได้สองวิธีง่ายๆ
- ติดตั้ง TailwindCSS เป็น dependency ในโปรเจกต์:
npm install -D tailwindcss@latest postcss@latest autoprefixer@latest 2. สร้างไฟล์ config TailwindCSS สำหรับโปรเจกต์ จะสร้าง tailwind.config.js ใน root directory:
npx tailwindcss init จากนั้นคุณต้องแก้ไขไฟล์ config รวม purge paths และเปิด jit mode:
module.exports = { purge: [], mode: 'jit', purge: ['./pages/**/*.{js,ts,jsx,tsx}', './components/**/*.{js,ts,jsx,tsx}'], darkMode: false, // or 'media' or 'class' theme: { extend: {}, }, variants: { extend: {}, }, plugins: [], }คุณใช้ purge เพื่อลบสไตล์ที่ไม่ต้องการออกจากโปรเจกต์ตอน build ช่วยลดขนาด CSS bundle
jit คือโหมดใหม่ของ TailwindCSS ที่คุณสามารถระบุ class names แบบไดนามิกในโค้ดได้
ตัวอย่าง หากคุณต้องการขนาดข้อความเป็น 10px (ซึ่งไม่มีใน TailwindCSS modules) คุณสามารถเขียน text-[10px] ใน class names และมันจะสะท้อนอัตโนมัติ ไม่ต้องเขียน custom style attributes อีกต่อไป 💯
ต่อไป นำเข้า Tailwind styles ใน root _app.js:
// pages/_app.js import '../styles/globals.css' import 'tailwindcss/tailwind.css' function MyApp({ Component, pageProps }) { return <Component {...pageProps} /> } export default MyAppจากนั้นรวม Tailwind core CSS ใน stylesheet ระดับราก:
/* ./styles/globals.css */ @tailwind base; @tailwind components; @tailwind utilities;ด้วยนี้ คุณตั้งค่า TailwindCSS สำหรับโปรเจกต์สำเร็จแล้ว
Markup และ Styling สำหรับหน้าติดต่อ

เราจะสร้างเว็บเพจทั้งหมดด้วย Tailwind ผมได้หน้านี้โดยตรงจาก Tailwind Master Kit ซึ่งเป็นไลบรารีคอมโพเนนต์และเทมเพลตสำหรับโปรเจกต์เว็บ Tailwind
มาเข้าใจ HTML ของหน้า (โดยพื้นฐานคือฟอร์มติดต่อ) ว่าทุกอย่างถูกนำไปใช้อย่างไร:
<form class="rounded-lg shadow-xl flex flex-col px-8 py-8 bg-white dark:bg-blue-500"> <h1 class="text-2xl font-bold dark:text-gray-50">Send a message</h1> <label for="fullname" class="text-gray-500 font-light mt-8 dark:text-gray-50">Full name<span class="text-red-500 dark:text-gray-50">*</span></label> <input type="text" name="fullname" class="bg-transparent border-b py-2 pl-4 focus:outline-none focus:rounded-md focus:ring-1 ring-green-500 font-light text-gray-500" /> <label for="email" class="text-gray-500 font-light mt-4 dark:text-gray-50">E-mail<span class="text-red-500">*</span></label> <input type="email" name="email" class="bg-transparent border-b py-2 pl-4 focus:outline-none focus:rounded-md focus:ring-1 ring-green-500 font-light text-gray-500" /> <label for="subject" class="text-gray-500 font-light mt-4 dark:text-gray-50">Subject<span class="text-red-500">*</span></label> <input type="text" name="subject" class="bg-transparent border-b py-2 pl-4 focus:outline-none focus:rounded-md focus:ring-1 ring-green-500 font-light text-gray-500" /> <label for="message" class="text-gray-500 font-light mt-4 dark:text-gray-50">Message<span class="text-red-500">*</span></label> <textarea name="message" class="bg-transparent border-b py-2 pl-4 focus:outline-none focus:rounded-md focus:ring-1 ring-green-500 font-light text-gray-500"></textarea> <div class="flex flex-row items-center justify-start"> <button class="px-10 mt-8 py-2 bg-[#130F49] text-gray-50 font-light rounded-md text-lg flex flex-row items-center"> Send <svg width="24" height="24" viewBox="0 0 24 24" class="text-cyan-500 ml-2" fill="currentColor" xmlns="https://www.w3.org/2000/svg"> <path d="M9.00967 5.12761H11.0097C12.1142 5.12761 13.468 5.89682 14.0335 6.8457L16.5089 11H21.0097C21.562 11 22.0097 11.4477 22.0097 12C22.0097 12.5523 21.562 13 21.0097 13H16.4138L13.9383 17.1543C13.3729 18.1032 12.0191 18.8724 10.9145 18.8724H8.91454L12.4138 13H5.42485L3.99036 15.4529H1.99036L4.00967 12L4.00967 11.967L2.00967 8.54712H4.00967L5.44417 11H12.5089L9.00967 5.12761Z" fill="currentColor" /> </svg> </button> </div> </form>ฟอร์มมี 4 ฟิลด์:
- Full Name
- Subject
- Message
ทุกฟิลด์เป็นบังคับ – และเราจะ validate ด้วยภายหลัง คุณคาดหวังว่าผู้ใช้จะให้รายละเอียดทั้งหมดขณะส่งอีเมล
เพื่อจับฟิลด์ เราจะใช้ React's useState() hook เพื่อให้แน่ใจว่าข้อมูลของเรายังคงอยู่ในแอปพลิเคชัน
export default function ContactUs() { const [fullname, setFullname] = useState(""); const [email, setEmail] = useState(""); const [subject, setSubject] = useState(""); const [message, setMessage] = useState(""); return ( <form onSubmit={handleSubmit} className="rounded-lg shadow-xl flex flex-col px-8 py-8 bg-white dark:bg-blue-500" > <h1 className="text-2xl font-bold dark:text-gray-50"> Send a message </h1> <label htmlFor="fullname" className="text-gray-500 font-light mt-8 dark:text-gray-50" > Full name<span className="text-red-500 dark:text-gray-50">*</span> </label> <input type="text" value={fullname} onChange={(e) => { setFullname(e.target.value); }} name="fullname" className="bg-transparent border-b py-2 pl-4 focus:outline-none focus:rounded-md focus:ring-1 ring-green-500 font-light text-gray-500" /> <label htmlFor="email" className="text-gray-500 font-light mt-4 dark:text-gray-50" > E-mail<span className="text-red-500">*</span> </label> <input type="email" name="email" value={email} onChange={(e) => { setEmail(e.target.value); }} className="bg-transparent border-b py-2 pl-4 focus:outline-none focus:rounded-md focus:ring-1 ring-green-500 font-light text-gray-500" /> <label htmlFor="subject" className="text-gray-500 font-light mt-4 dark:text-gray-50" > Subject<span className="text-red-500">*</span> </label> <input type="text" name="subject" value={subject} onChange={(e) => { setSubject(e.target.value); }} className="bg-transparent border-b py-2 pl-4 focus:outline-none focus:rounded-md focus:ring-1 ring-green-500 font-light text-gray-500" /> <label htmlFor="message" className="text-gray-500 font-light mt-4 dark:text-gray-50" > Message<span className="text-red-500">*</span> </label> <textarea name="message" value={message} onChange={(e) => { setMessage(e.target.value); }} className="bg-transparent border-b py-2 pl-4 focus:outline-none focus:rounded-md focus:ring-1 ring-green-500 font-light text-gray-500" ></textarea> <div className="flex flex-row items-center justify-start"> <button type="submit" className="px-10 mt-8 py-2 bg-[#130F49] text-gray-50 font-light rounded-md text-lg flex flex-row items-center" > Submit <svg width="24" height="24" viewBox="0 0 24 24" className="text-cyan-500 ml-2" fill="currentColor" xmlns="https://www.w3.org/2000/svg" > <path d="M9.00967 5.12761H11.0097C12.1142 5.12761 13.468 5.89682 14.0335 6.8457L16.5089 11H21.0097C21.562 11 22.0097 11.4477 22.0097 12C22.0097 12.5523 21.562 13 21.0097 13H16.4138L13.9383 17.1543C13.3729 18.1032 12.0191 18.8724 10.9145 18.8724H8.91454L12.4138 13H5.42485L3.99036 15.4529H1.99036L4.00967 12L4.00967 11.967L2.00967 8.54712H4.00967L5.44417 11H12.5089L9.00967 5.12761Z" fill="currentColor" /> </svg> </button> </div> </form> ) }สังเกตแอตทริบิวต์ฟอร์ม onSubmit={handleSubmit} นี่คือฟังก์ชันที่เราจะส่งอีเมลจริงผ่าน SendGrid แต่ก่อนหน้านั้น มาสร้างโปรเจกต์ SendGrid และดึง API keys
วิธีตั้งค่าโปรเจกต์ SendGrid

ก่อนอื่น คุณแค่ต้องไปที่หน้าแรกของ SendGrid และสมัครบัญชี (หากยังไม่มี)
หลังจากสร้างบัญชีสำเร็จ ให้ลงทะเบียน API Key ทำได้ที่นี่
Sendgrid กำหนดให้คุณสร้าง Sender Identity เพื่อป้องกันสแปมและเมลฆ่าลาย คุณทำได้ที่หน้า Sendgrid Identity คลิก Create New Sender เพื่อสร้าง sender identity
คุณจะต้องกรอกฟอร์มโดยละเอียด แค่กรอกเสร็จและกด submit สุดท้ายแค่ยืนยันอีเมล และเสร็จแล้ว
เมื่อคุณได้ API keys แล้ว สร้างไฟล์ .env.local ใน local environment และวางโค้ดต่อไปนี้:
SENDGRID_API_KEY= YOUR_API_KEY_HERE แทนที่ YOUR_API_KEY_HERE ด้วย API key ที่คุณเพิ่งดึงมา
วิธีสร้าง Serverless API Route
การสร้าง serverless API route ทำได้ง่ายกับ Next.js
ไปที่ /pages/api และภายในโฟลเดอร์ api สร้างไฟล์ชื่อ sendgrid.js
import sendgrid from "@sendgrid/mail"; sendgrid.setApiKey(process.env.SENDGRID_API_KEY); async function sendEmail(req, res) { try { // console.log("REQ.BODY", req.body); await sendgrid.send({ to: "mannuarora7000@gmail.com", // อีเมลของคุณที่จะรับอีเมล from: "manuarorawork@gmail.com", // อีเมลเว็บไซต์ของคุณที่นี่ subject: `${req.body.subject}`, html: `<div>You've got a mail</div>`, }); } catch (error) { // console.log(error); return res.status(error.statusCode || 500).json({ error: error.message }); } return res.status(200).json({ error: "" }); } export default sendEmail; SendGrid กำหนดให้เราเริ่มต้นออบเจกต์ sendgrid ด้วย API keys ผ่านเมธอด setApiKey() เริ่มต้นออบเจกต์ด้วย API key ของคุณ และคุณสามารถส่งอีเมลด้วยเมธอด send()
มีสี่ฟิลด์ที่จำเป็นใน body ของเมธอด send():
to– ที่อยู่อีเมลที่คุณต้องการให้อีเมลส่งถึงfrom– อีเมล SendGrid ที่คุณใช้สำหรับ Sender Identity Verification อีเมลของคุณจะถูกส่งจากอีเมลนี้subject– บรรทัดหัวข้อของอีเมลmessage– เนื้อหาข้อความของอีเมล
เราจะสร้างพารามิเตอร์สี่ตัวนี้ด้วยตัวเองเพื่อให้เราเข้าใจอีเมลของเราดีกว่า นี่คือโค้ดที่อัปเดตจากสนิปเป็ตเดียวกันข้างต้น:
import sendgrid from "@sendgrid/mail"; sendgrid.setApiKey(process.env.SENDGRID_API_KEY); async function sendEmail(req, res) { try { await sendgrid.send({ to: "youremail@gmail.com", // อีเมลของคุณที่จะรับอีเมล from: "youremail@gmail.com", // อีเมลเว็บไซต์ของคุณที่นี่ subject: `[Lead from website] : ${req.body.subject}`, html: `<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "https://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html lang="en"> <head> <meta charset="utf-8"> <title>The HTML5 Herald</title> <meta name="description" content="The HTML5 Herald"> <meta name="author" content="SitePoint"> <meta http-equiv="Content-Type" content="text/html charset=UTF-8" /> <link rel="stylesheet" href="css/styles.css?v=1.0"> </head> <body> <div class="img-container" style="display: flex;justify-content: center;align-items: center;border-radius: 5px;overflow: hidden; font-family: 'helvetica', 'ui-sans';"> </div> <div class="container" style="margin-left: 20px;margin-right: 20px;"> <h3>You've got a new mail from ${req.body.fullname}, their email is: ✉️${req.body.email} </h3> <div style="font-size: 16px;"> <p>Message:</p> <p>${req.body.message}</p> <br> </div> <img src="https://manuarora.in/logo.png" class="logo-image" style="height: 50px;width: 50px;border-radius: 5px;overflow: hidden;"> <p class="footer" style="font-size: 16px;padding-bottom: 20px;border-bottom: 1px solid #D1D5DB;">Regards<br>Manu Arora<br>Software Developer<br>+91 9587738861</p> <div class="footer-links" style="display: flex;justify-content: center;align-items: center;"> <a href="https://manuarora.in/" style="text-decoration: none;margin: 8px;color: #9CA3AF;">Website</a> <a href="https://manuarora.in/blog/" style="text-decoration: none;margin: 8px;color: #9CA3AF;">Blog</a> <a href="https://github.com/manuarora700/" style="text-decoration: none;margin: 8px;color: #9CA3AF;">GitHub</a> <a href="https://instagram.com/maninthere/" style="text-decoration: none;margin: 8px;color: #9CA3AF;">Instagram</a> <a href="https://linkedin.com/in/manuarora28/" style="text-decoration: none;margin: 8px;color: #9CA3AF;">LinkedIn</a> <a href="https://twitter.com/mannupaaji/" style="text-decoration: none;margin: 8px;color: #9CA3AF;">Twitter</a> </div> </div> </body> </html>`, }); } catch (error) { // console.log(error); return res.status(error.statusCode || 500).json({ error: error.message }); } return res.status(200).json({ error: "" }); } export default sendEmail; หากคุณต้องการส่ง html ใน body อีเมล คุณจะต้องใช้ inline styles ซึ่งมีในตัวอย่างแล้ว
ที่นี่ เราใช้เมธอด send() ของ SendGrid API เพื่อส่งอีเมล เราใช้ send() กับออบเจกต์ sendgrid ที่เราเริ่มต้นด้วย API key นี่ทำให้มั่นใจได้ว่าอีเมลของเราปลอดภัยและส่งเฉพาะโดย許可ของเรา
นอกจากนี้ เราห่อโค้ดไว้ในบล็อก try - catch นี่ทำให้แอปพลิเคชันของเราจัดการข้อยกเว้นและข้อผิดพลาดได้ถูกต้อง หากการส่งอีเมลล้มเหลว โค้ดจะตกไปที่บล็อก catch() ทันที และเราคืนค่า error object นี่บ่งชี้ว่ามีปัญหาบนแบ็กเอนด์
เมื่อดู API response จากแบ็กเอนด์ frontend จะตอบสนองตามนั้น และ UI จะเปลี่ยน
การจัดสไตล์ไปอยู่ในแอตทริบิวต์ html ภายใน body ของเมธอด send() คุณอยากจัดสไตล์อีเมลอย่างไรก็ขึ้นอยู่กับคุณ ที่นี่ผมรวมเทมเพลตง่ายๆ พร้อม footer ไป Twitter, Instagram, GitHub และเว็บไซต์ พร้อม body ข้อความต้นฉบับที่ผู้ใช้ปลายทางส่ง
ตอนนี้ API route ของเราพร้อมแล้ว มาไปที่ frontend เรียนรู้วิธีจัดการ response ให้ถูกต้อง
วิธีเรียก API และจัดการ Response
เนื่องจาก API route ของเราพร้อมแล้ว เราจะเรียก serverless API และดึง response
import React, { useState } from "react"; export default function ContactUs() { const [fullname, setFullname] = useState(""); const [email, setEmail] = useState(""); const [subject, setSubject] = useState(""); const [message, setMessage] = useState(""); const handleSubmit = async (e) => { e.preventDefault(); let isValidForm = handleValidation(); const res = await fetch("/api/sendgrid", { body: JSON.stringify({ email: email, fullname: fullname, subject: subject, message: message, }), headers: { "Content-Type": "application/json", }, method: "POST", }); const { error } = await res.json(); if (error) { console.log(error); return; } console.log(fullname, email, subject, message); }; return ( <main> <form class="rounded-lg shadow-xl flex flex-col px-8 py-8 bg-white dark:bg-blue-500"> <h1 class="text-2xl font-bold dark:text-gray-50">Send a message</h1> <label for="fullname" class="text-gray-500 font-light mt-8 dark:text-gray-50">Full name<span class="text-red-500 dark:text-gray-50">*</span></label> <input type="text" name="fullname" class="bg-transparent border-b py-2 pl-4 focus:outline-none focus:rounded-md focus:ring-1 ring-green-500 font-light text-gray-500" /> <label for="email" class="text-gray-500 font-light mt-4 dark:text-gray-50">E-mail<span class="text-red-500">*</span></label> <input type="email" name="email" class="bg-transparent border-b py-2 pl-4 focus:outline-none focus:rounded-md focus:ring-1 ring-green-500 font-light text-gray-500" /> <label for="subject" class="text-gray-500 font-light mt-4 dark:text-gray-50">Subject<span class="text-red-500">*</span></label> <input type="text" name="subject" class="bg-transparent border-b py-2 pl-4 focus:outline-none focus:rounded-md focus:ring-1 ring-green-500 font-light text-gray-500" /> <label for="message" class="text-gray-500 font-light mt-4 dark:text-gray-50">Message<span class="text-red-500">*</span></label> <textarea name="message" class="bg-transparent border-b py-2 pl-4 focus:outline-none focus:rounded-md focus:ring-1 ring-green-500 font-light text-gray-500"></textarea> <div class="flex flex-row items-center justify-start"> <button class="px-10 mt-8 py-2 bg-[#130F49] text-gray-50 font-light rounded-md text-lg flex flex-row items-center"> Send <svg width="24" height="24" viewBox="0 0 24 24" class="text-cyan-500 ml-2" fill="currentColor" xmlns="https://www.w3.org/2000/svg"> <path d="M9.00967 5.12761H11.0097C12.1142 5.12761 13.468 5.89682 14.0335 6.8457L16.5089 11H21.0097C21.562 11 22.0097 11.4477 22.0097 12C22.0097 12.5523 21.562 13 21.0097 13H16.4138L13.9383 17.1543C13.3729 18.1032 12.0191 18.8724 10.9145 18.8724H8.91454L12.4138 13H5.42485L3.99036 15.4529H1.99036L4.00967 12L4.00967 11.967L2.00967 8.54712H4.00967L5.44417 11H12.5089L9.00967 5.12761Z" fill="currentColor" /> </svg> </button> </div> </form> </main> ); } ที่นี่เราเรียก API ที่เราสร้างด้วย fetch ซึ่ง React จัดเตรียมไว้
Fetch เรียก serverless API ด้วย body อย่างนี้:
body: JSON.stringify({ email: email, fullname: fullname, subject: subject, message: message, })นี่คือฟิลด์ฟอร์มพร้อมข้อมูลฟอร์มที่กรอกแล้ว (จำได้ไหม useState()?) ซึ่งตอนนี้พร้อมใช้งานสำหรับเรา
API ตอบกลับด้วย success หรือ failure หากสำเร็จ อีเมลจะถูกส่ง – มิฉะนั้น เมลไม่ถูกส่ง
เพื่อให้ผู้ใช้ปลายทางรู้สถานะฟอร์ม เราต้องแสดง UI elements บางอย่าง แต่ก่อนหน้านั้น มาจัดการว่าจะเกิดอะไรขึ้นถ้ามีฟิลด์ว่าง
วิธีจัดการการตรวจสอบฟอร์มและทำให้ UI ตอบสนองต่อ API Response

เราต้องมั่นใจ 3 สิ่งที่นี่:
- ทุกฟิลด์ควรถูกกรอก – นั่นคือ เราไม่สามารถ submit ฟอร์มได้หากฟิลด์ไหนว่าง ผู้ใช้ต้องรู้ว่าทำไมฟอร์มถึงไม่ submit สำหรับนั้น เราจะแสดงข้อความข้อผิดพลาด
- ระหว่างที่ฟอร์มกำลัง submit ผู้ใช้ต้องรู้ว่ามีการประมวลผล กำลังดำเนินการ สำหรับนั้น เราจะเปลี่ยนข้อความปุ่มเมื่อฟอร์มกำลัง submit
- เมื่อฟอร์ม submit สำเร็จหรือล้มเหลว เราจะแสดงสถานะสุดท้ายที่ด้านล่างของฟอร์ม
มาสร้างเมธอด handleValidation() เพื่อตรวจสอบ validation:
const handleValidation = () => { let tempErrors = {}; let isValid = true; if (fullname.length <= 0) { tempErrors["fullname"] = true; isValid = false; } if (email.length <= 0) { tempErrors["email"] = true; isValid = false; } if (subject.length <= 0) { tempErrors["subject"] = true; isValid = false; } if (message.length <= 0) { tempErrors["message"] = true; isValid = false; } setErrors({ ...tempErrors }); console.log("errors", errors); return isValid; };ฟังก์ชันตรงไปตรงมา: เช็คทุกฟิลด์ และคืนค่า boolean isValid หากฟอร์ม valid
นอกจากนี้ เรารักษา state สำหรับทุกฟิลด์เพื่อแสดงข้อความข้อผิดพลาดท้ายที่สุด – โดยพื้นฐานเราบันทึกฟิลด์ที่มีข้อผิดพลาด
โค้ดสุดท้ายมีลักษณะประมาณนี้ พร้อมข้อความปุ่ม ข้อความข้อผิดพลาด และการ validate ฟอร์ม:
import React, { useState } from "react"; export default function ContactUs() { // States for contact form fields const [fullname, setFullname] = useState(""); const [email, setEmail] = useState(""); const [subject, setSubject] = useState(""); const [message, setMessage] = useState(""); // Form validation state const [errors, setErrors] = useState({}); // Setting button text on form submission const [buttonText, setButtonText] = useState("Send"); // Setting success or failure messages states const [showSuccessMessage, setShowSuccessMessage] = useState(false); const [showFailureMessage, setShowFailureMessage] = useState(false); // Validation check method const handleValidation = () => { let tempErrors = {}; let isValid = true; if (fullname.length <= 0) { tempErrors["fullname"] = true; isValid = false; } if (email.length <= 0) { tempErrors["email"] = true; isValid = false; } if (subject.length <= 0) { tempErrors["subject"] = true; isValid = false; } if (message.length <= 0) { tempErrors["message"] = true; isValid = false; } setErrors({ ...tempErrors }); console.log("errors", errors); return isValid; }; // Handling form submit const handleSubmit = async (e) => { e.preventDefault(); let isValidForm = handleValidation(); if (isValidForm) { setButtonText("Sending"); const res = await fetch("/api/sendgrid", { body: JSON.stringify({ email: email, fullname: fullname, subject: subject, message: message, }), headers: { "Content-Type": "application/json", }, method: "POST", }); const { error } = await res.json(); if (error) { console.log(error); setShowSuccessMessage(false); setShowFailureMessage(true); setButtonText("Send"); return; } setShowSuccessMessage(true); setShowFailureMessage(false); setButtonText("Send"); } console.log(fullname, email, subject, message); }; return ( <main> // Rest of the JSX code goes here. (With form fields) </main> ); } เมื่อฟอร์มส่งสำเร็จ เราจะได้ข้อความ response ที่ดีในฟอร์มติดต่อ เพื่อส่ง response นั้น เรามี state showSuccessMessage และ showFailureMessage หาก response จาก API route แบ็กเอนด์ ไม่มี property error หมายความว่าการ submit ฟอร์มสำเร็จและอีเมลถูกส่ง
ในกรณีนั้น showSuccessMessage ถูกตั้งเป็น True ซึ่งแสดง markup ที่เกี่ยวข้องใต้กล่องฟอร์ม หาก response body มี property error showFailureMessage ถูกตั้งเป็น True และข้อความที่เกี่ยวข้องจะแสดงบนหน้าจอ
ในทั้งกรณี success และ failure เราต้อง reset button text เป็น send แทน sending... สำหรับนั้น เราใช้ state setButtonText('send') ที่ตั้งข้อความปุ่มในกรณีล้มเหลวหรือสำเร็จ เราตั้งข้อความปุ่มเป็น sending... เมื่อปุ่ม Send ถูกคลิก
วิธีรับอีเมลและ UI Responses
เมื่ออีเมลส่งสำเร็จ เราจะได้ข้อความ success ในฟอร์มติดต่อเอง

และคุณจะได้รับอีเมลสำเร็จพร้อมเทมเพลตที่เราสร้าง ส่งมาอย่างปลอดภัยโดย SendGrid 💯

Environment Variables
โปรดทราบว่าเราใช้ API keys และ keys นั้นเป็นข้อมูลลับ หมายความว่าเราควรเก็บ secret หรือ API keys ใน environment variables เสมอ
เช่นที่เรามี .env.local สำหรับ local environment แล้ว hosting provider ก็ต้องรู้ API keys ด้วย
Vercel ให้วิธีง่ายในการเก็บ API keys บนแผงควบคุมโฮสติ้งเอง
เพื่อเก็บ API keys อย่างปลอดภัยในบัญชี Vercel ของคุณ ทำดังนี้:
- ไปที่หน้า projects
- ไปที่ settings
- ไปที่ Environment variables
- เพิ่มชื่อ environment variable ในกรณีนี้คือ
SENDGRID_API_KEYและเพิ่ม API key ที่ตรงกันในฟิลด์ value - Re-deploy แอปพลิเคชัน และโปรเจกต์จะทำงานใน production environment
Live Demo และ Source Code
นี่คือ source code และ live demo ของแอปพลิเคชัน:
Live Demo
Source Code
สรุป
SendGrid เป็นตัวเลือกที่ยอดเยี่ยมสำหรับส่งอีเมลจากเว็บไซต์ เมื่อคุณผสานกับ Next.js และ serverless API routes ของพวกเขา มันจะกลายเป็นเรื่องง่ายมากในการผสานฟอร์มในส่วนใดส่วนหนึ่งของเว็บไซต์ของคุณ
SendGrid ยังให้ตัวเลือกผสานเทมเพลตที่คุณสามารถมี custom themes สำหรับอีเมลของคุณ
มีตัวเลือกอื่นๆ สำหรับส่งอีเมลอย่าง Nodemailer ที่ผมเคยใช้ในอดีตและยังใช้สำหรับบางโปรเจกต์
ใช้เวลาประมาณหนึ่งชั่วโมงในการสร้างแอปพลิเคชันนี้ตั้งแต่ศูนย์ – ขอบคุณ Next.js, TailwindCSS, และ SendGrid สำหรับ workflow และ API semantics ที่เข้าใจง่ายมาก ขอบคุณ Tailwind Master Kit สำหรับ Contact Page UI ที่สวยงาม
หากคุณชอบบล็อกนี้ ลองนำไปใช้ในเว็บไซต์ของคุณเอง เพื่อให้คุณสามารถติดต่อผู้ใช้ปลายทางได้
หากคุณต้องการให้ feedback ติดต่อผมที่ Twitter handle หรือเยี่ยมเว็บไซต์ของผม
Happy coding. :)