Form liên hệ hữu ích trên trang web nếu bạn muốn người dùng tương tác qua email. Và có nhiều cách xây dựng chúng.
Truyền thống bạn dùng PHP gửi email qua server hoặc dịch vụ thứ ba lo phần logic email.
Nhưng bài này sẽ nói cách gửi email từ ứng dụng Next.js với SendGrid API.
Chúng ta sẽ tạo trang đơn giản – form liên hệ dùng React – có các trường input mà chúng ta sẽ validate trước khi gửi. Chúng ta kết nối form với SendGrid API lo việc gửi email cho bạn. Cuối ngày, bạn chỉ cần kiểm tra email tìm các truy vấn đó.
Tuy nhiên, nếu bạn chưa có dự án Next.js, bạn dễ dàng tạo và tích hợp với Vercel theo các bước:
- Tạo tài khoản Vercel và nhấp
New Project

2. Chọn template Next.js:

3. Đặt tên repository bất kỳ và nhấp create project. (Chọn GitHub, GitLab hoặc BitBucket cho versioning)

Làm theo 3 điểm trên, bạn sẽ có repository trên tài khoản versioning.
Tech Stack Chúng Ta Sẽ Dùng
- Next.js tạo trang landing page form liên hệ
- TailwindCSS tạo kiểu cho component
- SendGrid gửi email dùng API của họ
- Vercel host ứng dụng và CI/CD
Chúng ta dùng API routes của Nextjs xử lý sự kiện form. Module API cung cấp cách linh hoạt xử lý logic backend trong ứng dụng Next.js.
Mã viết trong thư mục API sẽ deploy dưới dạng Serverless function lên Vercel để host. Đọc thêm về Next.js API routes tại đây
Nếu đã có dự án Next.js muốn thiết lập form liên hệ hoạt động, tuyệt vời. Trong trường hợp đó, dễ dàng tạo trang và bắt đầu ngay.
Nhưng nếu chưa có dự án, cũng ok – vào Vercel tạo dự án Next.js starter và clone repository.
Luồng Ứng Dụng
Xem luồng ứng dụng – hoặc cách gửi email thực sự hoạt động:
- Người dùng cuối điền 4 trường bắt buộc và nhấp submit.
- Khi submit, hàm
handleSubmitđược kích hoạt. handleSubmitvalidate form cho trường input và kiểm tra không rỗng.- Nếu trường không rỗng, gọi API đến
api/sendgridnơi logic gửi email nằm. - Trong
api/sendgrid, module@sendgrid/mailkhởi tạo hàmsendnhận API key ứng dụng và gửi email với trường bắt buộc. - Nếu email gửi thành công, phản hồi
200gửi về client, ngược lại400. - Phản hồi xử lý ở frontend và hiển thị thông báo phù hợp.
Cách Thiết Lập TailwindCSS
Thiết lập TailwindCSS khá dễ, làm được theo 2 cách đơn giản.
- Cài TailwindCSS làm dependency trong dự án:
npm install -D tailwindcss@latest postcss@latest autoprefixer@latest 2. Khởi tạo file cấu hình TailwindCSS cho dự án. Sẽ tạo file tailwind.config.js ở thư mục gốc:
npx tailwindcss init Sau đó chỉnh file cấu hình, thêm đường dẫn purge, bật chế độ jit:
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: [], }Dùng purge loại bỏ style không cần thiết ở build time. Hữu ích nếu muốn giảm kích thước CSS bundle.
jit là chế độ TailwindCSS mới cho phép chỉ định classname động trong mã. Ví dụ, muốn text size 10px (chưa có trong module TailwindCSS), viết text-[10px] trong class name và nó tự động phản ánh. Không cần viết custom style nữa. 💯
Tiếp, import style Tailwind trong file _app.js gốc:
// pages/_app.js import '../styles/globals.css' import 'tailwindcss/tailwind.css' function MyApp({ Component, pageProps }) { return <Component {...pageProps} /> } export default MyAppSau đó nhúng core CSS Tailwind trong stylesheet gốc:
/* ./styles/globals.css */ @tailwind base; @tailwind components; @tailwind utilities;Với này, bạn đã thiết lập TailwindCSS thành công cho dự án.
Markup Và Tạo Kiểu Cho Trang Liên Hệ

Chúng ta xây dựng trang hoàn toàn bằng Tailwind. Tôi lấy trang trực tiếp từ Tailwind Master Kit – thư viện component và template cho dự án web Tailwind.
Hãy xem qua HTML của trang (cơ bản là form liên hệ) để hiểu triển khai:
<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>Form có 4 trường:
- Họ tên đầy đủ
- Chủ đề
- Tin nhắn
Tất cả trường bắt buộc – và chúng ta sẽ validate sau. Bạn mong đợi người dùng cung cấp đủ chi tiết khi gửi email.
Để lấy giá trị trường, dùng hook useState() của React đảm bảo dữ liệu tồn tại trong ứng dụng.
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> ) }Chú ý thuộc tính form onSubmit={handleSubmit}. Đây là hàm chúng ta sẽ gửi email qua SendGrid. Nhưng trước đó, hãy tạo dự án SendGrid và lấy API keys.
Cách Thiết Lập Dự Án SendGrid

Trước hết, truy cập trang chủ SendGrid và đăng ký tài khoản (nếu chưa có).
Sau khi tạo tài khoản thành công, đăng ký API Key. Làm tại đây.
Sendgrid yêu cầu tạo Sender Identity (Danh tính người gửi) chống spam và mail độc hại. Vào trang Sendgrid Identity và nhấp Create New Sender tạo sender identity.
Bạn sẽ điền form chi tiết. Hoàn thành form và nhấp submit. Cuối cùng, xác minh email và xong.
Sau khi lấy API keys, tạo file .env.local trong môi trường local và dán mã:
SENDGRID_API_KEY= YOUR_API_KEY_HERE Thay YOUR_API_KEY_HERE bằng API key vừa lấy.
Cách Tạo API Route Serverless
Tạo serverless API route khá dễ với Next.js.
Vào /pages/api và trong thư mục api tạo file 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", // Email nhận from: "manuarorawork@gmail.com", // Email website 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 yêu cầu khởi tạo đối tượng sendgrid với API key qua phương thức setApiKey(). Khởi tạo đối tượng với API key và gửi email với phương thức send().
Cơ bản có 4 trường bắt buộc trong body phương thức send():
to– Địa chỉ email nhậnfrom– Email SendGrid dùng cho Sender Identity Verification. Email gửi từ địa chỉ này.subject– Dòng chủ đề emailmessage– Nội dung email
Chúng ta sẽ tự xây dựng 4 tham số để hiểu email tốt hơn. Đây là mã cập nhật từ đoạn trên:
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", // Email nhận from: "youremail@gmail.com", // Email website 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; Nếu muốn gửi html trong body email, phải dùng inline style như ví dụ.
Ở đây, chúng ta dùng phương thức send() của SendGrid API để gửi email. Dùng send() với đối tượng sendgrid khởi tạo bằng API key. Điều này đảm bảo email an toàn và chỉ gửi bằng phép của chúng ta.
Cũng, bọc mã trong block try - catch. Điều này đảm bảo ứng dụng xử lý ngoại lệ và lỗi đúng. Nếu gửi email thất bại, mã rơi vào block catch() và trả về đối tượng error. Điều này báo hiệu vấn đề ở backend.
Nhìn phản hồi API từ backend, frontend phản hồi tương ứng và UI thay đổi.
Style đi vào thuộc tính html trong body phương thức send(). Cách bạn muốn style email hoàn toàn tùy bạn. Ở đây, tôi đưa template đơn giản với footer Twitter, Instagram, GitHub, website, cùng body tin nhắn gốc người dùng gửi.
API route đã sẵn sàng, chuyển sang frontend học cách xử lý phản hồi đúng.
Cách Gọi API Và Xử Lý Phản Hồi
Vì API route đã sẵn sàng, chúng ta sẽ gọi serverless API và lấy phản hồi.
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> ); } Ở đây gọi API vừa tạo với fetch, do React cung cấp.
Fetch gọi serverless API với body như sau:
body: JSON.stringify({ email: email, fullname: fullname, subject: subject, message: message, })Đây là trường form với dữ liệu form đã điền (nhớ useState()?) hiện có sẵn cho chúng ta.
API phản hồi thành công hoặc thất bại. Nếu thành công, email được gửi – ngược lại, mail không gửi.
Để người dùng cuối biết trạng thái form, cần hiển thị một số yếu tố UI. Nhưng trước đó, cần xử lý nếu có trường rỗng.
Cách Xử Lý Validate Form Và Làm UI Phản Hồi Theo Phản Hồi API

Cần đảm bảo 3 điều:
- Tất cả trường phải điền – không submit form nếu trường nào rỗng. Người dùng phải biết tại sao form không submit. Vì vậy hiển thị thông báo lỗi.
- Khi form đang submit, người dùng phải biết đang xử lý. Thay đổi chữ nút khi form đang submit.
- Khi form submit thành công hoặc thất bại, hiển thị trạng thái cuối dưới form.
Tạo phương thức handleValidation() kiểm tra validate:
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; };Hàm khá trực quan: kiểm tra tất cả trường và trả về boolean isValid nếu form hợp lệ.
Cũng, duy trì state cho tất cả trường để hiển thị thông báo lỗi – cơ bản lưu trường chứa lỗi.
Mã cuối trông như sau, với chữ nút, thông báo lỗi, validate form:
import React, { useState } from "react"; export default function ContactUs() { // States cho trường form liên hệ const [fullname, setFullname] = useState(""); const [email, setEmail] = useState(""); const [subject, setSubject] = useState(""); const [message, setMessage] = useState(""); // State validate form const [errors, setErrors] = useState({}); // Thiết lập chữ nút khi submit form const [buttonText, setButtonText] = useState("Send"); // Thiết lập state thông báo thành công/thất bại const [showSuccessMessage, setShowSuccessMessage] = useState(false); const [showFailureMessage, setShowFailureMessage] = useState(false); // Phương thức kiểm tra validate 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; }; // Xử lý submit form 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> // Phần JSX còn lại ở đây. (Với trường form) </main> ); } Khi form gửi thành công, nhận thông báo đẹp ở UI. Để gửi phản hồi đó, có showSuccessMessage và showFailureMessage. Nếu phản hồi từ backend API route KHÔNG chứa thuộc tính error, nghĩa là submit form thành công và email đã gửi.
Trong trường hợp đó, showSuccessMessage đặt True, hiển thị markup tương ứng ngay dưới hộp Form. Nếu body phản hồi chứa thuộc tính error, showFailureMessage đặt True và thông báo tương ứng hiển thị trên màn hình.
Cả hai trường hợp thành công và thất bại, phải reset chữ nút về send thay vì sending.... Dùng state setButtonText('send') đặt chữ nút nếu thất bại hoặc thành công. Chữ nút đặt sending... khi nhấp nút Send.
Cách Nhận Email Và Phản Hồi UI
Khi email gửi thành công, nhận thông báo thành công trong form liên hệ.

Và bạn nhận thành công email với template vừa tạo, được SendGrid gửi an toàn 💯

Biến Môi Trường
Lưu ý chúng ta dùng API key và key nhạy cảm. Nghĩa là luôn lưu secret hoặc API key trong biến môi trường.
Như đã có .env.local cho môi trường local, nhà cung cấp host cũng cần biết API key.
Vercel cung cấp cách dễ dàng lưu API key trên bảng điều khiển host.
Để lưu API key an toàn trong tài khoản Vercel, làm sau:
- Vào trang projects
- Vào settings
- Vào Environment variables
- Thêm tên biến môi trường, trong trường hợp là
SENDGRID_API_KEY, và thêm API key tương ứng vào trường value. - Re-deploy ứng dụng và dự án sẽ hoạt động trong môi trường production.
Demo Trực Tiếp Và Mã Nguồn
Đây là mã nguồn và demo trực tiếp ứng dụng:
Live Demo
Source Code
Kết Luận
SendGrid là lựa chọn tuyệt vời gửi email từ trang web. Khi tích hợp với Next.js và serverless API routes của họ, việc tích hợp form vào bất kỳ phần nào trang web trở nên cực kỳ dễ dàng.
SendGrid cũng cho tùy chọn tích hợp template để có theme tùy chỉnh cho email.
Còn các tùy chọn khác gửi email như Nodemailer tôi đã dùng trước và vẫn dùng cho một số dự án.
Tôi mất khoảng một giờ xây dựng ứng dụng từ đầu – nhờ Next.js, TailwindCSS, và SendGrid với quy trình và API semantics cực trực quan. Cũng cảm ơn Tailwind Master Kit cho UI trang liên hệ đẹp.
Nếu thích blog này, thử triển khai trên trang web của bạn để tiếp cận người dùng cuối.
Nếu muốn phản hồi, liên hệ qua Twitter handle hoặc truy cập Website của tôi
Happy coding. :)