كيفية إنشاء نموذج تواصل باستخدام SendGrid و Next.js
مقدمة: لماذا تحتاج إلى نموذج تواصل احترافي؟
يُعد نموذج التواصل من أهم العناصر في أي موقع إلكتروني، لأنه يوفّر قناة مباشرة بين الزائر وصاحب الموقع دون الحاجة إلى عرض البريد الإلكتروني بشكل مكشوف. هذا الأسلوب لا يحسّن تجربة المستخدم فقط، بل يساعد أيضاً في تقليل الرسائل العشوائية وتنظيم الاستفسارات الواردة بطريقة أكثر احترافية.
في هذا الدليل، سنتعرف على كيفية بناء Contact Form باستخدام Next.js وربطه مع SendGrid لإرسال الرسائل عبر البريد الإلكتروني. كما سنعتمد على TailwindCSS لتنسيق الواجهة، وعلى Vercel لاستضافة التطبيق وتشغيل Serverless Functions بكفاءة.
![]()
إنشاء مشروع Next.js وربطه مع Vercel
إذا لم يكن لديك مشروع جاهز، يمكنك البدء بسرعة عبر Vercel وإنشاء مشروع جديد مبني على Next.js. هذه الخطوة تتيح لك تجهيز بيئة عمل حديثة مع سهولة النشر لاحقاً.
- أنشئ حساباً على Vercel ثم اضغط على
New Project. - اختر قالب
Next.jsلبدء المشروع. - حدّد اسم المستودع، ثم اربطه مع GitHub أو GitLab أو Bitbucket.



التقنيات المستخدمة في المشروع
| التقنية | الاستخدام |
|---|---|
| Next.js | بناء صفحة نموذج التواصل ومعالجة الطلبات عبر API Routes |
| TailwindCSS | تنسيق الواجهة بسرعة ومرونة |
| SendGrid | إرسال الرسائل البريدية عبر API آمن واحترافي |
| Vercel | استضافة التطبيق وتشغيل Serverless Functions |
تعتمد الفكرة الأساسية هنا على Next.js API Routes، إذ يمكننا كتابة منطق الخادم داخل مجلد /pages/api، ليتم نشره تلقائياً على شكل Serverless Function عند رفع المشروع إلى Vercel.
كيف تعمل آلية إرسال الرسائل؟
لفهم الصورة الكاملة، إليك تدفق العمل داخل التطبيق:
- يقوم المستخدم بملء الحقول الأربعة الإلزامية.
- عند الضغط على زر الإرسال، يتم تشغيل الدالة
handleSubmit. - تتحقق الدالة أولاً من صحة المدخلات وعدم وجود حقول فارغة.
- إذا كانت البيانات سليمة، يتم إرسال طلب
POSTإلى/api/sendgrid. - داخل هذا المسار، يتم استخدام مكتبة
@sendgrid/mailلإرسال الرسالة. - إذا نجحت العملية، يعود الخادم باستجابة
200، وإلا يتم إرجاع خطأ مناسب. - في الواجهة الأمامية، تُعرض رسالة نجاح أو فشل للمستخدم.
إعداد TailwindCSS داخل مشروع Next.js
إعداد TailwindCSS بسيط وسريع. أولاً، ثبّت الحزم المطلوبة:
npm install -D tailwindcss@latest postcss@latest autoprefixer@latest
بعد ذلك، أنشئ ملف الإعدادات:
npx tailwindcss init
ثم عدّل ملف tailwind.config.js ليشمل المسارات المطلوبة ويفعّل نمط jit:
module.exports = {
purge: [],
mode: 'jit',
purge: [
'./pages/**/*.{js,ts,jsx,tsx}',
'./components/**/*.{js,ts,jsx,tsx}',
],
darkMode: false,
theme: {
extend: {},
},
variants: {
extend: {},
},
plugins: [],
}
يساعد purge على إزالة الأنماط غير المستخدمة عند البناء النهائي، مما يقلل حجم ملفات CSS. أما jit فيمنحك مرونة أكبر لاستخدام قيم مخصصة مباشرة داخل الأصناف مثل text-[10px].
الخطوة التالية هي استيراد ملفات Tailwind داخل _app.js:
// pages/_app.js
import '../styles/globals.css'
import 'tailwindcss/tailwind.css'
function MyApp({ Component, pageProps }) {
return <Component {...pageProps} />
}
export default MyApp
ثم أضف توجيهات Tailwind داخل ملف globals.css:
/* ./styles/globals.css */
@tailwind base;
@tailwind components;
@tailwind utilities;
بناء واجهة نموذج التواصل باستخدام TailwindCSS
الواجهة تتكون من أربعة حقول أساسية: الاسم الكامل، البريد الإلكتروني، الموضوع، والرسالة. وجميعها حقول إلزامية لضمان وصول استفسار واضح ومتكامل.

<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
</button>
</div>
</form>
ربط الحقول مع React باستخدام useState
لكي يتم حفظ قيم الحقول والتعامل معها برمجياً، نستخدم useState داخل مكوّن الصفحة:
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
</button>
</div>
</form>
)
}
إعداد حساب SendGrid والحصول على API Key
لبدء إرسال الرسائل، تحتاج إلى حساب على SendGrid. بعد التسجيل، أنشئ API Key جديدة، ثم فعّل Sender Identity حتى يسمح لك النظام بإرسال الرسائل بشكل موثوق ويحد من إساءة الاستخدام.

بعد الحصول على المفتاح، أنشئ ملف .env.local داخل المشروع وأضف السطر التالي:
SENDGRID_API_KEY=YOUR_API_KEY_HERE
استبدل YOUR_API_KEY_HERE بالمفتاح الحقيقي الخاص بك.
إنشاء Serverless API Route في Next.js
الآن سننشئ مسار API يتولى إرسال الرسائل. داخل المجلد /pages/api أنشئ ملفاً باسم sendgrid.js:
import sendgrid from "@sendgrid/mail";
sendgrid.setApiKey(process.env.SENDGRID_API_KEY);
async function sendEmail(req, res) {
try {
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) {
return res.status(error.statusCode || 500).json({ error: error.message });
}
return res.status(200).json({ error: "" });
}
export default sendEmail;
في هذا المثال، يتم تهيئة مكتبة @sendgrid/mail باستخدام المفتاح المخزن في متغيرات البيئة، ثم تُرسل الرسالة عبر الدالة send().
الحقول الأساسية المطلوبة داخل send()
to: البريد الإلكتروني الذي ستصل إليه الرسالة.from: البريد المعتمد في Sender Identity.subject: عنوان الرسالة.htmlأوtext: محتوى الرسالة.
مثال متقدم على رسالة HTML
إذا كنت ترغب في إرسال رسالة منسقة داخل البريد الإلكتروني، يمكنك تمرير قالب HTML كامل مع أنماط inline styles:
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" "http://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) {
return res.status(error.statusCode || 500).json({ error: error.message });
}
return res.status(200).json({ error: "" });
}
export default sendEmail;
استخدام try/catch هنا مهم جداً، لأنه يضمن التعامل السليم مع الأخطاء وإرجاع استجابة واضحة للواجهة الأمامية.
استدعاء API من الواجهة الأمامية
بعد تجهيز المسار الخلفي، حان وقت إرسال البيانات من النموذج إلى الخادم باستخدام fetch:
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>
</form>
</main>
);
}
يتم هنا تحويل القيم إلى JSON ثم إرسالها إلى المسار الخلفي. إذا عادت الاستجابة بدون خطأ، فهذا يعني أن الرسالة أُرسلت بنجاح.
التحقق من صحة الحقول وتحسين تجربة المستخدم
وجود نموذج جميل لا يكفي وحده، بل يجب أن يكون واضحاً وسهل الاستخدام. من أهم الجوانب هنا منع إرسال النموذج إذا كانت أي من الحقول فارغة، مع توضيح السبب للمستخدم.

دالة التحقق من المدخلات
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;
};
هذه الدالة تعيد قيمة منطقية تحدد ما إذا كان النموذج صالحاً للإرسال أم لا، كما تحفظ حالة الأخطاء لعرضها لاحقاً ضمن الواجهة.
النسخة الكاملة مع حالات النجاح والفشل
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 [errors, setErrors] = useState({});
const [buttonText, setButtonText] = useState("Send");
const [showSuccessMessage, setShowSuccessMessage] = useState(false);
const [showFailureMessage, setShowFailureMessage] = useState(false);
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;
};
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>
);
}
في هذا السيناريو:
- يتغير نص الزر إلى
Sendingأثناء تنفيذ الطلب. - تظهر رسالة نجاح عند وصول الاستجابة بنجاح.
- تظهر رسالة فشل إذا تعذر إرسال البريد.
- يعود الزر إلى حالته الأصلية بعد انتهاء العملية في كلتا الحالتين.
كيف تظهر النتيجة للمستخدم؟
بعد نجاح الإرسال، يحصل المستخدم على تغذية راجعة واضحة داخل الواجهة، وهو أمر مهم جداً لتحسين الثقة وتجربة الاستخدام.

وفي الجهة الأخرى، ستصلك الرسالة عبر البريد الإلكتروني باستخدام القالب الذي جهزته مسبقاً:

إدارة متغيرات البيئة بأمان
مفاتيح API بيانات حساسة ولا ينبغي وضعها مباشرة داخل الشيفرة المصدرية. لذلك يجب استخدام متغيرات البيئة دائماً.
على البيئة المحلية استخدمنا ملف .env.local، أما في Vercel فيمكنك إضافة المفتاح عبر:
- الانتقال إلى صفحة المشروع.
- فتح Settings.
- الدخول إلى Environment Variables.
- إضافة المتغير
SENDGRID_API_KEYمع قيمته الصحيحة. - إعادة نشر المشروع ليعمل بشكل سليم في بيئة الإنتاج.
نصائح عملية لتحسين جودة نموذج التواصل
إذا كنت تستهدف موقعاً احترافياً ومتوافقاً مع سياسات Google AdSense، فمن الأفضل ألا تكتفي بالتنفيذ الأساسي. إليك بعض التحسينات المفيدة:
- أضف تحققاً أقوى من البريد الإلكتروني بصيغة
regexأو مكتبات متخصصة. - استخدم
rate limitingأوreCAPTCHAلتقليل الرسائل المزعجة. - اعرض رسائل خطأ مفهومة للمستخدم بدلاً من الرسائل التقنية.
- نظّم محتوى البريد بحيث يتضمن اسم المرسل والموضوع والرسالة بوضوح.
- فصل منطق الإرسال عن مكوّن الواجهة لتحسين قابلية الصيانة.
لماذا يعد SendGrid خياراً جيداً مع Next.js؟
الدمج بين SendGrid و Next.js يمنحك حلاً عملياً لبناء نماذج تواصل حديثة دون الحاجة إلى إعداد خادم تقليدي كامل. كما أن Serverless Functions تقلل التعقيد التشغيلي، بينما يوفر SendGrid طبقة موثوقة لإرسال الرسائل وتتبعها.
هذا النهج مناسب جداً لصفحات الهبوط، مواقع الشركات، المعارض الشخصية، والمتاجر التي تحتاج إلى قناة تواصل سريعة وفعالة.
الخلاصة التقنية
من الناحية التقنية، يُعد بناء Contact Form باستخدام Next.js و SendGrid خياراً ذكياً يجمع بين سهولة التطوير، الأمان، وسرعة النشر. استخدام API Routes يقلل الحاجة إلى بنية خلفية منفصلة، بينما يتيح SendGrid إرسال الرسائل بشكل موثوق وقابل للتوسع. وإذا أضفت التحقق من المدخلات وتحسينات الأمان وتجربة المستخدم، فستحصل على نموذج احترافي فعلاً يصلح للإنتاج ويلائم المواقع المستهدفة للقبول في Google AdSense.