كيفية إنشاء موقع معرض أعمال احترافي باستخدام Next.js وTailwindCSS
يمثل موقع معرض الأعمال الشخصي نقطة انطلاق مهمة لأي مطور ويب أو محترف تقني يبحث عن فرصة عمل أو يريد بناء حضور رقمي قوي. فبدلاً من الاكتفاء بإرسال السيرة الذاتية، يمنحك الموقع الشخصي مساحة عملية لعرض مهاراتك، ومشاريعك، وخبراتك، وطريقتك في التفكير، وكل ذلك ضمن تجربة احترافية تعكس هويتك التقنية.
في هذا الدليل، سنشرح كيفية بناء موقع Portfolio حديث باستخدام Next.js وTailwindCSS، مع توضيح البنية العامة للمشروع، وأهم الصفحات، وآلية دعم الوضع الليلي، وجلب أحدث المستودعات من GitHub، ثم نشر المشروع على Vercel. الهدف هنا ليس مجرد ترجمة الفكرة، بل تقديم شرح عربي احترافي ومفيد يساعدك على تنفيذ موقع فعلي قابل للتخصيص والنشر.
![]()
لماذا تحتاج إلى موقع معرض أعمال شخصي؟
امتلاك موقع شخصي لم يعد خياراً إضافياً، بل أصبح أداة فعالة لرفع فرصك في الظهور أمام مسؤولي التوظيف والعملاء المحتملين. فعندما يرى الزائر نماذج عملك بشكل مباشر، يصبح من الأسهل تقييم مستواك العملي بعيداً عن العبارات العامة الموجودة في السيرة الذاتية.
- عرض المشاريع بطريقة منظمة وجذابة.
- إبراز المهارات التقنية والأدوات التي تتقنها.
- تقديم نبذة احترافية عنك وعن مسارك المهني.
- نشر مقالات تقنية أو شروحات أو مقتطفات كود قابلة لإعادة الاستخدام.
- تعزيز العلامة الشخصية وبناء حضور موثوق على الإنترنت.
والميزة الأهم أن موقعك الشخصي يمنحك حرية كاملة في تقديم نفسك بالطريقة التي تناسبك، بدلاً من الاعتماد فقط على منصات خارجية محدودة القالب.
التقنيات المستخدمة في بناء الموقع
إطار Next.js
يعد Next.js واحداً من أفضل الأطر المبنية فوق React لإنشاء مواقع سريعة وعملية وصديقة لمحركات البحث. ومن أبرز مزاياه دعمه لـServer-Side Rendering، وهي ميزة تساعد على تحسين الأرشفة وظهور المحتوى بشكل أوضح لمحركات البحث.
كما يوفر مزايا جاهزة مثل:
- تحسين الصور.
- إنشاء الصفحات والمسارات بسهولة.
- أداء مرتفع في التحميل والعرض.
- مرونة كبيرة بين العرض الثابت والديناميكي.
إطار التنسيق TailwindCSS
أما TailwindCSS فهو إطار CSS عملي يعتمد على أصناف جاهزة منخفضة المستوى يمكن تضمينها مباشرة داخل عناصر الواجهة. هذا الأسلوب يقلل الوقت المستغرق في كتابة التنسيقات التقليدية، ويسرّع بناء الواجهات بشكل ملحوظ.
من أهم مزاياه:
- السرعة في بناء النماذج الأولية والواجهات الإنتاجية.
- سهولة دعم التصميم المتجاوب.
- إمكانية تخصيص الألوان والمسافات والخطوط.
- تقليل الحاجة إلى ملفات CSS كبيرة ومعقدة.
على سبيل المثال، الكود <div className="text-sm md:text-xl"></div> يعني أن حجم النص سيكون صغيراً على الشاشات الصغيرة، وأكبر على الشاشات المتوسطة وما فوق.
مكتبة Rough Notation
استُخدمت مكتبة react-rough-notation لإبراز بعض النصوص في القسم التعريفي الأول Hero Section. هذا النوع من التأثيرات البصرية يلفت انتباه الزائر بسرعة إلى أهم الكلمات أو الأوصاف التي تريد ترسيخها.
النشر عبر Vercel
تم اختيار Vercel لكونه من أفضل الحلول لنشر تطبيقات Next.js. إذ يوفر تجربة نشر سهلة، ويدعم التكامل المستمر CI/CD، بحيث يتم تحديث الموقع تلقائياً عند كل عملية push إلى المستودع.
أهم مزايا موقع معرض الأعمال

هذا النوع من المواقع لا يحتاج إلى تعقيد مبالغ فيه، لكنه يحتاج إلى أساس قوي وخصائص عملية. ومن أبرز الميزات التي ينبغي أن تتوفر فيه:
- الوضع الليلي: دعم التبديل بين النمط الفاتح والداكن.
- بنية مبنية على
Next.js: للاستفادة من السرعة وتحسين السيو. - تنسيق قابل للتخصيص: بفضل
TailwindCSSيمكن تعديل الألوان والهوية البصرية بسهولة. - مكوّن مخصص للبيانات الوصفية: لتخصيص وسوم
metaلكل صفحة. - تصميم متجاوب: ليظهر الموقع بشكل ممتاز على الحاسوب والجهاز اللوحي والهاتف.
الصفحات الأساسية التي يجب تضمينها
أي موقع معرض أعمال احترافي يجب أن يتضمن مجموعة صفحات أساسية توصل الرسالة بسرعة ووضوح:
- الصفحة الرئيسية: نقطة الدخول الأولى للزائر.
- من أنا: نبذة تعريفية ومهارات وروابط اجتماعية.
- الخبرات: سجل الخبرة المهنية والمشاريع ذات الصلة.
- المشاريع: عرض مرئي لأبرز الأعمال المنجزة.
- التواصل: نموذج أو وسائل اتصال مباشرة.
هيكلة المشروع والمجلدات
من نقاط قوة Next.js سهولة تنظيم الملفات والمكوّنات. ويمكن اعتماد بنية بسيطة وواضحة كما يلي:
components: يحتوي على المكوّنات القابلة لإعادة الاستخدام مثل شريط التنقل والقسم التعريفي والتذييل.public: للملفات الثابتة مثل الصور والخطوط والملفات العامة.styles: للتنسيقات العامة ودمج مكتبةTailwindCSS.pages: يضم جميع صفحات الموقع، وكل ملف بداخله يمثل مساراً تلقائياً.
هذه البنية تسهّل التطوير والصيانة والتوسع لاحقاً.
مكوّن الحاوية الرئيسي ContainerBlock
يُعد المكوّن <ContainerBlock /> الأب الذي يغلّف بقية أجزاء الصفحات. ووظيفته لا تقتصر على تنسيق التخطيط العام، بل يوفّر أيضاً إدارة مركزية للبيانات الوصفية مثل العنوان والوصف والصورة ووسوم المشاركة الاجتماعية.
هذا الأسلوب مفيد جداً من ناحية السيو، لأنه يسمح بتخصيص عنوان ووصف مختلفين لكل صفحة بدلاً من الاكتفاء بإعدادات عامة للموقع بأكمله.
import React from "react";
import Head from "next/head";
import { useRouter } from "next/router";
import Navbar from "./Navbar";
import Footer from "./Footer";
export default function ContainerBlock({ children, ...customMeta }) {
const router = useRouter();
const meta = {
title: "Manu Arora - Developer, Writer, Creator and YouTuber",
description: `I've been developing websites for 5 years straight. Get in touch with me to know more.`,
image: "/avatar.png",
type: "website",
...customMeta,
};
return (
<div>
<Head>
<title>{meta.title}</title>
<meta name="robots" content="follow, index" />
<meta content={meta.description} name="description" />
<meta property="og:url" content={`https://yourwebsite.com${router.asPath}`} />
<link rel="canonical" href={`https://yourwebsite.com${router.asPath}`} />
<meta property="og:type" content={meta.type} />
<meta property="og:site_name" content="Manu Arora" />
<meta property="og:description" content={meta.description} />
<meta property="og:title" content={meta.title} />
<meta property="og:image" content={meta.image} />
<meta name="twitter:card" content="summary_large_image" />
<meta name="twitter:site" content="@mannupaaji" />
<meta name="twitter:title" content={meta.title} />
<meta name="twitter:description" content={meta.description} />
<meta name="twitter:image" content={meta.image} />
{meta.date && (
<meta property="article:published_time" content={meta.date} />
)}
</Head>
<main className="dark:bg-gray-800 w-full">
<Navbar />
<div>{children}</div>
<Footer />
</main>
</div>
);
}
وبعد إنشاء ملف ContainerBlock.js يمكنك استخدامه لتغليف أي صفحة وتمرير بياناتها الوصفية بسهولة:
import Head from "next/head";
import styles from "../styles/Home.module.css";
import ContainerBlock from "../components/ContainerBlock";
import FavouriteProjects from "../components/FavouriteProjects";
import LatestCode from "../components/LatestCode";
import Hero from "../components/Hero";
export default function Home() {
return (
<ContainerBlock
title="Manu Arora - Developer, Writer, Creator"
description="Building a template with Next.js and Tailwindcss - for FreeCodeCamp users."
>
<Hero />
<FavouriteProjects />
<LatestCode />
</ContainerBlock>
);
}
كيفية تفعيل الوضع الليلي Dark Mode

دعم الوضع الليلي أصبح من الخصائص المتوقعة في أغلب المواقع الحديثة. ولتنفيذه هنا تم الاعتماد على الحزمة next-themes، والتي توفر آلية بسيطة لإدارة السمة الحالية وتمريرها إلى جميع الأبناء عبر ThemeProvider.
import "../styles/globals.css";
import { ThemeProvider } from "next-themes";
function MyApp({ Component, pageProps }) {
return (
<ThemeProvider defaultTheme="light" attribute="class">
<Component {...pageProps} />
</ThemeProvider>
);
}
export default MyApp;

ثم نضيف زر التبديل داخل مكوّن Navbar حتى يبقى متاحاً في جميع الصفحات:
import React, { useEffect, useState } from "react";
import Link from "next/link";
import { useTheme } from "next-themes";
import { useRouter } from "next/router";
export default function Navbar() {
const router = useRouter();
console.log(router.asPath);
const { theme, setTheme } = useTheme();
const [mounted, setMounted] = useState(false);
useEffect(() => {
setMounted(true);
}, []);
return (
<div className="max-w-6xl mx-auto px-4 py-10 md:py-20">
<div className="flex md:flex-row justify-between items-center">
<button
aria-label="Toggle Dark Mode"
type="button"
className="w-10 h-10 p-3 rounded focus:outline-none"
onClick={() => setTheme(theme === "dark" ? "light" : "dark")}
>
{mounted && (
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
fill="currentColor"
stroke="currentColor"
className="w-4 h-4 text-yellow-500 dark:text-yellow-500"
>
{theme === "dark" ? (
<path
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth={2}
d="M12 3v1m0 16v1m9-9h-1M4 12H3m15.364 6.364l-.707-.707M6.343 6.343l-.707-.707m12.728 0l-.707.707M6.343 17.657l-.707.707M16 12a4 4 0 11-8 0 4 4 0 018 0z"
/>
) : (
<path
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth={2}
d="M20.354 15.354A9 9 0 018.646 3.646 9.003 9.003 0 0012 21a9.003 9.003 0 008.354-5.646z"
/>
)}
</svg>
)}
</button>
</div>
</div>
);
}
هذه الإضافة صغيرة من ناحية التنفيذ، لكنها تعزز تجربة الاستخدام بشكل واضح، خاصة في الأجهزة المحمولة أو أثناء التصفح الليلي.
بناء قسم البداية Hero Section

القسم الافتتاحي هو أول ما يراه الزائر، ولذلك يجب أن يجيب بسرعة عن سؤالين أساسيين: من أنت؟ وماذا تقدم؟
تم استخدام مكتبة react-rough-notation لإضافة إبراز حركي لبعض الكلمات المفتاحية. ويمكن إنشاء مكوّن مخصص باسم RainbowHighlight ليكون قابلاً لإعادة الاستخدام في أي مكان داخل المشروع.
import React from "react";
import { RoughNotation } from "react-rough-notation";
export const RainbowHighlight = ({ color, children }) => {
const animationDuration = Math.floor(30 * children.length);
return (
<RoughNotation
type="highlight"
multiline={true}
padding={[0, 2]}
iterations={1}
animationDuration={animationDuration}
color={color}
>
{children}
</RoughNotation>
);
};
import React from "react";
import { RoughNotation, RoughNotationGroup } from "react-rough-notation";
import { RainbowHighlight } from "./RainbowHighlight";
export default function Hero() {
const colors = ["#F59E0B", "#84CC16", "#10B981", "#3B82F6"];
return (
<div className="flex flex-row justify-center items-start overflow-hidden">
<div className="w-full md:w-1/2 mx-auto text-center md:text-left lg:p-20">
<RoughNotationGroup show={true}>
<RainbowHighlight color={colors[0]}>
<h1 className="text-4xl md:text-8xl font-bold text-gray-700 dark:text-gray-200 my-2">
Developer.
</h1>
</RainbowHighlight>
</RoughNotationGroup>
</div>
</div>
);
}
يفضل أن يحتوي هذا القسم على:
- اسمك أو صفتك المهنية بوضوح.
- وصف مختصر لما تقدمه.
- زر للانتقال إلى المشاريع أو صفحة التواصل.
- صورة شخصية أو رسم تعبيري يعكس هويتك المهنية.
جلب أحدث المستودعات من GitHub

إضافة آخر المشاريع البرمجية من GitHub تمنح الموقع حداثة مستمرة وتربطه مباشرة بنشاطك التقني الحقيقي. ويمكن تنفيذ ذلك عبر GitHub API بسهولة.
الطلب التالي يجلب المستودعات مع الاعتماد على الترتيب بحسب تاريخ التحديث:
const res = await axios.get(
`https://api.github.com/search/repositories?q=user:${username}+sort:author-date-asc`
);
وبعد وصول البيانات، يمكن الاكتفاء بآخر 6 مستودعات فقط باستخدام splice():
let repos = res.data.items;
let latestSixRepos = repos.splice(0, 6);
return latestSixRepos;
وهذا هو شكل الدالة الكامل:
import axios from "axios";
const getLatestRepos = async (data) => {
console.log("data", data);
try {
const username = data.githubUsername;
const res = await axios.get(
`https://api.github.com/search/repositories?q=user:${username}+sort:author-date-asc`
);
let repos = res.data.items;
let latestSixRepos = repos.splice(0, 6);
return latestSixRepos;
} catch (err) {
console.log(err);
}
};
export default getLatestRepos;
ثم يمكن استخدام النتائج داخل مكوّن لعرض البطاقات:
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-8 max-w-6xl mx-auto px-10 lg:-mt-10 gap-y-20">
{repos && repos.map((latestRepo, idx) => (
<GithubRepoCard latestRepo={latestRepo} key={idx} />
))}
</div>
const GithubRepoCard = ({ latestRepo }) => {
return (
<div className="github-repo">
<h1 className="font-semibold text-xl dark:text-gray-200 text-gray-700">
{latestRepo.name}
</h1>
<p className="text-base font-normal my-4 text-gray-500">
{latestRepo.description}
</p>
<a
href={latestRepo.clone_url}
className="font-semibold group flex flex-row space-x-2 w-full items-center"
>
<p>View Repository</p>
<div className="transform group-hover:translate-x-2 transition duration-300">
→
</div>
</a>
</div>
);
};
لكن هناك نقطة مهمة: يفرض GitHub API حداً معيناً لعدد الطلبات لكل عنوان IP. وإذا كنت تتوقع عدداً أكبر من الطلبات، فمن الأفضل إنشاء تطبيق على GitHub واستخدام Auth Tokens لرفع هذا الحد وتقليل احتمالات فشل الاستدعاءات.
إضافة صفحة المشاريع بشكل احترافي

قسم المشاريع هو غالباً أكثر جزء يهتم به مسؤول التوظيف، لأنه يجيب عملياً عن سؤال: ماذا أنجزت؟ لذلك يفضل أن يكون بصرياً، بسيطاً، وسهل التصفح.
في هذا المثال، تم تقسيم المشاريع ضمن شبكة grid من عمودين على الشاشات الكبيرة، وتتحول تلقائياً إلى عمود واحد على الهواتف. كما أن تأثير hover على الصور يضيف حيوية خفيفة دون إزعاج المستخدم.
import React from "react";
export default function Projects() {
return (
<section className="bg-white dark:bg-gray-800">
<div className="max-w-6xl mx-auto h-48 bg-white dark:bg-gray-800">
<h1 className="text-5xl md:text-9xl font-bold py-20 text-center md:text-left">
Projects
</h1>
</div>
<div className="bg-[#F1F1F1] dark:bg-gray-900">
<div className="max-w-6xl mx-auto grid grid-cols-1 md:grid-cols-2 gap-8 py-20 pb-40">
<a href="https://tailwindmasterkit.com" className="w-full block shadow-2xl">
<div className="relative overflow-hidden">
<img
src="/tmk.jpg"
alt="portfolio"
className="transform hover:scale-125 transition duration-2000 ease-out"
/>
<h1 className="absolute top-10 left-10 text-gray-50 font-bold text-xl bg-red-500 rounded-md px-2">
Tailwind Master Kit
</h1>
<h1 className="absolute bottom-10 left-10 text-gray-50 font-bold text-xl">
01
</h1>
</div>
</a>
</div>
</div>
</section>
);
}
لتحسين هذا القسم فعلياً، احرص على أن يتضمن كل مشروع:
- صورة واضحة أو لقطة شاشة حقيقية.
- اسم المشروع.
- وصف قصير يوضح المشكلة والحل.
- رابط إلى النسخة الحية أو المستودع.
- ذكر التقنيات المستخدمة مثل
Next.jsأوReactأوNode.js.
بناء صفحة التواصل

صفحة التواصل يجب أن تكون مباشرة وواضحة، لأن الغاية منها تحويل الزائر المهتم إلى فرصة حقيقية. ويُستحسن أن تحتوي على أكثر من وسيلة تواصل، مثل البريد الإلكتروني، وروابط الحسابات المهنية، ونموذج بسيط وسريع.
في المثال التالي تم استخدام مكوّن جاهز مبني على TailwindCSS لتسريع العمل:
import React from "react";
export default function Contact() {
return (
<section>
<div className="max-w-6xl mx-auto h-48 bg-white dark:bg-gray-800 antialiased">
<h1 className="text-5xl md:text-9xl font-bold py-20 text-center md:text-left">
Contact
</h1>
</div>
<div className="relative z-10 rounded-md shadow-md bg-[#02044A] p-4 md:p-10 lg:p-20 max-w-6xl mx-auto mb-20 -mt-4">
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
<div className="md:ml-4">
<header>
<h1 className="text-gray-50 font-semibold text-2xl">
Get in touch, let's talk.
</h1>
<p className="font-light text-base text-gray-200 mt-2">
Fill in the details and I'll get back to you as soon as I can.
</p>
</header>
<form className="form rounded-lg bg-white p-4 flex flex-col">
<label htmlFor="name" className="text-sm text-gray-600 mx-4">
Your Name
</label>
<input
type="text"
className="font-light rounded-md border focus:outline-none py-2 mt-2 px-1 mx-4 focus:ring-2 focus:border-none ring-blue-500"
name="name"
/>
<label htmlFor="email" className="text-sm text-gray-600 mx-4 mt-4">
Email
</label>
<input
type="text"
className="font-light rounded-md border focus:outline-none py-2 mt-2 px-1 mx-4 focus:ring-2 focus:border-none ring-blue-500"
name="email"
/>
<button
type="submit"
className="bg-blue-500 rounded-md w-1/2 mx-4 mt-8 py-2 text-gray-50 text-xs font-bold"
>
Send Message
</button>
</form>
</div>
</div>
</div>
</section>
);
}
من الناحية العملية، يفضل ربط النموذج لاحقاً بخدمة إرسال رسائل مثل EmailJS أو واجهة API مخصصة، مع التحقق من المدخلات وإظهار رسائل نجاح أو فشل واضحة للمستخدم.
خطوات نشر الموقع على Vercel
نشر المشروع من أسهل المراحل عند استخدام Next.js مع Vercel. ويمكن اختصار الخطوات كالتالي:
- استنساخ المستودع:
git clone https://github.com/manuarora700/simple-developer-portfolio-website
- تثبيت الاعتماديات:
npm install
- تشغيل بيئة التطوير المحلية:
npm run dev
- تخصيص المحتوى بإضافة مشاريعك، وروابطك، ومعلوماتك التعليمية، والبيانات الوصفية الخاصة بك.
- رفع التعديلات إلى مستودعك البعيد:
git add *
git commit -m "add changes to the cloned repo"
git push
- إنشاء حساب على
Vercelأو تسجيل الدخول. - ربط مستودع
GitHubالجديد بلوحةVercel. - سيتم نشر المشروع تلقائياً ومنحك رابطاً جاهزاً للمعاينة والمشاركة.

بعد ذلك يمكنك مشاركة الرابط مع الأصدقاء أو إضافته إلى سيرتك الذاتية أو ربطه بنطاق مخصص ليظهر بصورة أكثر مهنية.
نصائح مهمة لتحسين السيو والقبول في AdSense
إذا كان هدفك إنشاء موقع معرض أعمال احترافي وقابل لتحقيق الدخل أو القبول في Google AdSense، فهناك مجموعة ممارسات مهمة يجب الالتزام بها:
- اكتب محتوى أصلياً يشرح مشاريعك وخبراتك بلغة بشرية واضحة.
- أنشئ صفحات حقيقية مثل من نحن، سياسة الخصوصية، تواصل معنا، والمشاريع.
- استخدم عناوين واضحة ومنظمة عبر
<h2>و<h3>. - أضف أوصافاً دقيقة للصور عبر خاصية
alt. - اجعل الموقع سريعاً ومتوافقاً مع الهواتف.
- لا تملأ الصفحات بعناصر فارغة أو محتوى ضعيف القيمة.
- خصص لكل صفحة عنواناً ووصفاً فريدين باستخدام وسوم
meta.
كلما كان الموقع واضح الغرض، متماسك البنية، وغنياً بالمعلومات الفعلية، زادت فرص قبوله وثقة محركات البحث به.
الخلاصة التقنية
بناء موقع معرض أعمال باستخدام Next.js وTailwindCSS هو خيار ذكي لمن يريد موقعاً سريعاً، مرناً، وسهل التطوير. يجمع هذا النهج بين الأداء العالي، وسهولة تحسين السيو، والقدرة على إنشاء واجهات جذابة دون تعقيد كبير. والأهم من ذلك أن الموقع لا ينبغي أن يكون مجرد واجهة جميلة، بل منصة عملية تعرض خبرتك بوضوح، وتدعم صورتك المهنية، وتمنح الزائر سبباً حقيقياً للتواصل معك أو ترشيحك.