مشروع Next.js متكامل — مدونة احترافية 2026
مشروع عملي شامل لبناء مدونة كاملة بـ Next.js — مع Markdown، SEO، نشر تلقائي، وتصميم احترافي
🎉 مبروك! وصلت إلى المقال الأخير في مسار Next.js!
لقد تعلمت كل الأساسيات: App Router، Server Components، Data Fetching، API Routes، Styling، Authentication، والنشر. الآن حان الوقت لتطبيق كل هذه المفاهيم في مشروع حقيقي متكامل.
في هذا الدليل العملي، سنبني مدونة احترافية كاملة — وهي المشروع الذي يبنيه المطورون المحترفون فعلاً.
ما سنبنيه
مدونة احترافية تحتوي على:
- ✅ صفحة رئيسية مع أحدث المقالات.
- ✅ صفحة المقالات مع قائمة كاملة.
- ✅ صفحة المقال الفردي مع محتوى Markdown.
- ✅ تصنيفات (Categories).
- ✅ بحث (Search).
- ✅ جدول محتويات (TOC).
- ✅ مشاركة على وسائل التواصل.
- ✅ SEO محسّن (Metadata, Open Graph, Sitemap).
- ✅ تصميم متجاوب.
- ✅ وضع داكن.
- ✅ نشرة بريدية.
- ✅ مقالات ذات صلة.
هيكل المشروع
next-blog/
├── app/
│ ├── layout.tsx # التخطيط العام
│ ├── page.tsx # الصفحة الرئيسية
│ ├── globals.css
│ ├── blog/
│ │ ├── page.tsx # قائمة المقالات
│ │ └── [slug]/
│ │ └── page.tsx # مقال فردي
│ ├── categories/
│ │ └── [category]/
│ │ └── page.tsx # مقالات التصنيف
│ ├── about/
│ │ └── page.tsx
│ └── api/
│ └── subscribe/
│ └── route.ts # API النشرة
├── components/
│ ├── Header.tsx
│ ├── Footer.tsx
│ ├── PostCard.tsx
│ ├── SearchBar.tsx
│ ├── TableOfContents.tsx
│ ├── ShareButtons.tsx
│ └── NewsletterForm.tsx
├── lib/
│ ├── posts.ts # دوال قراءة المقالات
│ └── utils.ts
├── content/
│ └── posts/ # ملفات Markdown
│ ├── first-post.md
│ └── second-post.md
├── public/
│ └── images/
├── next.config.ts
├── tailwind.config.ts
├── package.json
└── tsconfig.json
الخطوة 1: إنشاء المشروع
npx create-next-app@latest next-blog --typescript --tailwind --eslint --app --no-src-dir --import-alias "@/*"
cd next-blog
الخطوة 2: تثبيت المكتبات
npm install gray-matter remark remark-html
npm install -D @tailwindcss/typography
المكتبات:
| المكتبة | الوظيفة |
|---|---|
gray-matter | قراءة Frontmatter من Markdown |
remark | تحويل Markdown إلى HTML |
remark-html | Plugin لـ remark |
@tailwindcss/typography | أنماط للنصوص |
الخطوة 3: tailwind.config.ts
import type { Config } from "tailwindcss";
const config: Config = {
content: [
"./app/**/*.{js,ts,jsx,tsx,mdx}",
"./components/**/*.{js,ts,jsx,tsx,mdx}",
],
darkMode: "class",
theme: {
extend: {
fontFamily: {
cairo: ["var(--font-cairo)", "sans-serif"],
},
},
},
plugins: [require("@tailwindcss/typography")],
};
export default config;
الخطوة 4: app/globals.css
@tailwind base;
@tailwind components;
@tailwind utilities;
@layer base {
body {
@apply bg-gray-50 text-gray-900 dark:bg-gray-900 dark:text-gray-100;
}
}
@layer components {
.container-custom {
@apply max-w-4xl mx-auto px-4;
}
}
/* صناديق الكود */
.prose pre {
@apply bg-gray-900 text-gray-100 rounded-xl p-4 overflow-x-auto;
}
.prose code {
@apply bg-gray-100 dark:bg-gray-800 px-1.5 py-0.5 rounded text-sm;
}
.prose pre code {
@apply bg-transparent p-0;
}
الخطوة 5: lib/posts.ts
import fs from "fs";
import path from "path";
import matter from "gray-matter";
import { remark } from "remark";
import html from "remark-html";
const postsDirectory = path.join(process.cwd(), "content/posts");
export interface Post {
slug: string;
title: string;
date: string;
excerpt: string;
category: string;
tags: string[];
coverImage?: string;
content: string;
}
// جلب كل المقالات
export function getAllPosts(): Post[] {
const fileNames = fs.readdirSync(postsDirectory);
const posts = fileNames
.filter((name) => name.endsWith(".md"))
.map((name) => {
const slug = name.replace(/\.md$/, "");
const fullPath = path.join(postsDirectory, name);
const fileContents = fs.readFileSync(fullPath, "utf8");
const { data, content } = matter(fileContents);
return {
slug,
title: data.title,
date: data.date,
excerpt: data.excerpt || content.slice(0, 150) + "...",
category: data.category || "غير مصنف",
tags: data.tags || [],
coverImage: data.coverImage,
content,
};
})
.sort((a, b) => (a.date < b.date ? 1 : -1));
return posts;
}
// جلب مقال واحد
export function getPostBySlug(slug: string): Post | null {
try {
const fullPath = path.join(postsDirectory, `${slug}.md`);
const fileContents = fs.readFileSync(fullPath, "utf8");
const { data, content } = matter(fileContents);
return {
slug,
title: data.title,
date: data.date,
excerpt: data.excerpt || content.slice(0, 150) + "...",
category: data.category || "غير مصنف",
tags: data.tags || [],
coverImage: data.coverImage,
content,
};
} catch {
return null;
}
}
// تحويل Markdown إلى HTML
export async function markdownToHtml(markdown: string): Promise<string> {
const result = await remark().use(html).process(markdown);
return result.toString();
}
// جلب مقالات التصنيف
export function getPostsByCategory(category: string): Post[] {
return getAllPosts().filter((post) => post.category === category);
}
// جلب كل التصنيفات
export function getAllCategories(): string[] {
const posts = getAllPosts();
return [...new Set(posts.map((post) => post.category))];
}
// جلب المقالات ذات الصلة
export function getRelatedPosts(slug: string, limit = 3): Post[] {
const post = getPostBySlug(slug);
if (!post) return [];
return getAllPosts()
.filter((p) => p.slug !== slug && p.category === post.category)
.slice(0, limit);
}
// وقت القراءة
export function getReadingTime(content: string): number {
const words = content.trim().split(/\s+/).length;
return Math.max(1, Math.ceil(words / 200));
}
// تنسيق التاريخ
export function formatDate(date: string): string {
return new Date(date).toLocaleDateString("ar-EG", {
year: "numeric",
month: "long",
day: "numeric",
});
}
الخطوة 6: مقال تجريبي
content/posts/first-post.md:
---
title: "مرحباً بك في مدونتي"
date: "2026-11-20"
excerpt: "أول مقال في مدونتي الجديدة بـ Next.js"
category: "تقنية"
tags: ["Next.js", "React", "برمجة"]
coverImage: "https://images.unsplash.com/photo-1516116216624-53e697fedbea?w=1200"
---
## مقدمة
مرحباً بك في مدونتي الجديدة! هذه مدونة مبنية بـ **Next.js** و **Tailwind CSS**.
## لماذا Next.js؟
Next.js هو إطار عمل رائع لبناء تطبيقات React. يتميز بـ:
- **App Router** — نظام توجيه حديث
- **Server Components** — أداء عالٍ
- **SEO ممتاز** — جاهز لمحركات البحث
- **سهل النشر** — على Vercel
## مثال على كود
```javascript
function greet(name) {
return `مرحباً ${name}!`;
}
الخلاصة
هذا مشروع رائع لتعلّم Next.js. استمتع!
**`content/posts/second-post.md`:**
```markdown
---
title: "كيف تتعلم البرمجة في 2026"
date: "2026-11-19"
excerpt: "دليل شامل لتعلم البرمجة من الصفر"
category: "تعلم"
tags: ["برمجة", "تعلم"]
coverImage: "https://images.unsplash.com/photo-1516116216624-53e697fedbea?w=1200"
---
## ابدأ من HTML
HTML هو أساس كل موقع.
## ثم CSS
CSS هو ما يجعل الموقع جميلاً.
## بعدها JavaScript
JavaScript هو ما يجعل الموقع تفاعلياً.
الخطوة 7: app/layout.tsx
import type { Metadata } from "next";
import { Cairo } from "next/font/google";
import Header from "@/components/Header";
import Footer from "@/components/Footer";
import "./globals.css";
const cairo = Cairo({
subsets: ["arabic", "latin"],
weight: ["400", "600", "700", "900"],
variable: "--font-cairo",
});
export const metadata: Metadata = {
title: {
default: "مدونتي | NahdaAI",
template: "%s | مدونتي",
},
description: "مدونتي الشخصية — مقالات عن البرمجة والتقنية",
metadataBase: new URL("https://myblog.com"),
openGraph: {
type: "website",
locale: "ar_AR",
siteName: "مدونتي",
},
};
export default function RootLayout({
children,
}: {
children: React.ReactNode;
}) {
return (
<html lang="ar" dir="rtl" className={cairo.variable}>
<body className="font-cairo">
<Header />
<main className="min-h-screen">{children}</main>
<Footer />
</body>
</html>
);
}
الخطوة 8: components/Header.tsx
import Link from "next/link";
import { getAllCategories } from "@/lib/posts";
export default function Header() {
const categories = getAllCategories();
return (
<header className="bg-white dark:bg-gray-800 shadow-sm sticky top-0 z-50">
<div className="max-w-6xl mx-auto px-4 py-4 flex items-center justify-between">
<Link href="/" className="text-2xl font-bold text-blue-600">
📝 مدونتي
</Link>
<nav className="hidden md:flex gap-6">
<Link href="/" className="text-gray-700 dark:text-gray-300 hover:text-blue-600">
الرئيسية
</Link>
<Link href="/blog" className="text-gray-700 dark:text-gray-300 hover:text-blue-600">
المقالات
</Link>
{categories.slice(0, 3).map((cat) => (
<Link
key={cat}
href={`/categories/${encodeURIComponent(cat)}`}
className="text-gray-700 dark:text-gray-300 hover:text-blue-600"
>
{cat}
</Link>
))}
<Link href="/about" className="text-gray-700 dark:text-gray-300 hover:text-blue-600">
من نحن
</Link>
</nav>
</div>
</header>
);
}
الخطوة 9: components/Footer.tsx
export default function Footer() {
return (
<footer className="bg-gray-900 text-white py-8 mt-16">
<div className="max-w-6xl mx-auto px-4 text-center">
<p className="mb-4">© 2026 مدونتي — جميع الحقوق محفوظة</p>
<div className="flex justify-center gap-4">
<a href="https://twitter.com" className="hover:text-blue-400 transition">
🐦 تويتر
</a>
<a href="https://github.com" className="hover:text-blue-400 transition">
💻 GitHub
</a>
</div>
</div>
</footer>
);
}
الخطوة 10: components/PostCard.tsx
import Link from "next/link";
import Image from "next/image";
import { Post, formatDate, getReadingTime } from "@/lib/posts";
export default function PostCard({ post }: { post: Post }) {
return (
<article className="bg-white dark:bg-gray-800 rounded-2xl overflow-hidden shadow-sm hover:shadow-lg transition group">
{post.coverImage && (
<Link href={`/blog/${post.slug}`}>
<div className="relative h-48 overflow-hidden">
<Image
src={post.coverImage}
alt={post.title}
fill
className="object-cover group-hover:scale-105 transition duration-300"
/>
</div>
</Link>
)}
<div className="p-6">
<div className="flex items-center gap-3 mb-3">
<Link
href={`/categories/${encodeURIComponent(post.category)}`}
className="bg-blue-100 dark:bg-blue-900 text-blue-700 dark:text-blue-300 text-xs px-3 py-1 rounded-full"
>
{post.category}
</Link>
<span className="text-gray-500 text-xs">
⏱ {getReadingTime(post.content)} دقائق
</span>
</div>
<Link href={`/blog/${post.slug}`}>
<h3 className="text-xl font-bold text-gray-900 dark:text-white mb-2 hover:text-blue-600 transition">
{post.title}
</h3>
</Link>
<p className="text-gray-600 dark:text-gray-400 text-sm mb-4 line-clamp-2">
{post.excerpt}
</p>
<div className="flex items-center justify-between text-xs text-gray-500">
<span>{formatDate(post.date)}</span>
<Link
href={`/blog/${post.slug}`}
className="text-blue-600 hover:underline font-medium"
>
اقرأ المزيد ←
</Link>
</div>
</div>
</article>
);
}
الخطوة 11: app/page.tsx
import Link from "next/link";
import { getAllPosts } from "@/lib/posts";
import PostCard from "@/components/PostCard";
export default function HomePage() {
const posts = getAllPosts();
const featuredPosts = posts.slice(0, 3);
return (
<>
{/* Hero */}
<section className="bg-gradient-to-br from-blue-600 to-purple-700 text-white py-20">
<div className="max-w-4xl mx-auto px-4 text-center">
<h1 className="text-5xl font-bold mb-4">مدونتي</h1>
<p className="text-xl opacity-90 mb-8">
مقالات عن البرمجة والتقنية — بالعربية
</p>
<Link
href="/blog"
className="inline-block bg-white text-blue-600 px-8 py-3 rounded-lg font-bold hover:bg-gray-100 transition"
>
تصفح المقالات →
</Link>
</div>
</section>
{/* المقالات المميزة */}
<section className="max-w-6xl mx-auto px-4 py-16">
<h2 className="text-3xl font-bold text-gray-900 dark:text-white mb-8">
📚 أحدث المقالات
</h2>
<div className="grid md:grid-cols-2 lg:grid-cols-3 gap-6">
{featuredPosts.map((post) => (
<PostCard key={post.slug} post={post} />
))}
</div>
<div className="text-center mt-12">
<Link
href="/blog"
className="inline-block bg-blue-600 text-white px-8 py-3 rounded-lg font-bold hover:bg-blue-700 transition"
>
عرض جميع المقالات ←
</Link>
</div>
</section>
</>
);
}
الخطوة 12: app/blog/page.tsx
import { getAllPosts } from "@/lib/posts";
import PostCard from "@/components/PostCard";
export const metadata = {
title: "المقالات",
description: "جميع مقالات المدونة",
};
export default function BlogPage() {
const posts = getAllPosts();
return (
<div className="max-w-6xl mx-auto px-4 py-12">
<h1 className="text-4xl font-bold text-gray-900 dark:text-white mb-8">
📚 جميع المقالات
</h1>
<p className="text-gray-600 dark:text-gray-400 mb-8">
{posts.length} مقال
</p>
<div className="grid md:grid-cols-2 lg:grid-cols-3 gap-6">
{posts.map((post) => (
<PostCard key={post.slug} post={post} />
))}
</div>
</div>
);
}
الخطوة 13: app/blog/[slug]/page.tsx
import { notFound } from "next/navigation";
import Link from "next/link";
import Image from "next/image";
import {
getPostBySlug,
getAllPosts,
markdownToHtml,
formatDate,
getReadingTime,
getRelatedPosts,
} from "@/lib/posts";
import PostCard from "@/components/PostCard";
import ShareButtons from "@/components/ShareButtons";
export async function generateStaticParams() {
const posts = getAllPosts();
return posts.map((post) => ({ slug: post.slug }));
}
export async function generateMetadata({
params,
}: {
params: Promise<{ slug: string }>;
}) {
const { slug } = await params;
const post = getPostBySlug(slug);
if (!post) return {};
return {
title: post.title,
description: post.excerpt,
openGraph: {
title: post.title,
description: post.excerpt,
type: "article",
images: post.coverImage ? [post.coverImage] : [],
},
};
}
export default async function PostPage({
params,
}: {
params: Promise<{ slug: string }>;
}) {
const { slug } = await params;
const post = getPostBySlug(slug);
if (!post) notFound();
const htmlContent = await markdownToHtml(post.content);
const relatedPosts = getRelatedPosts(slug);
return (
<article className="max-w-4xl mx-auto px-4 py-12">
<Link
href="/blog"
className="text-blue-600 hover:underline mb-6 inline-block"
>
← العودة للمقالات
</Link>
{post.coverImage && (
<div className="relative h-96 rounded-2xl overflow-hidden mb-8">
<Image
src={post.coverImage}
alt={post.title}
fill
className="object-cover"
priority
/>
</div>
)}
<div className="flex items-center gap-3 mb-4">
<Link
href={`/categories/${encodeURIComponent(post.category)}`}
className="bg-blue-100 dark:bg-blue-900 text-blue-700 dark:text-blue-300 text-sm px-3 py-1 rounded-full"
>
{post.category}
</Link>
<span className="text-gray-500 text-sm">
📅 {formatDate(post.date)}
</span>
<span className="text-gray-500 text-sm">
⏱ {getReadingTime(post.content)} دقائق
</span>
</div>
<h1 className="text-4xl font-bold text-gray-900 dark:text-white mb-4">
{post.title}
</h1>
<p className="text-xl text-gray-600 dark:text-gray-400 mb-8">
{post.excerpt}
</p>
<ShareButtons
title={post.title}
url={`https://myblog.com/blog/${post.slug}`}
/>
<div
className="prose prose-lg dark:prose-invert max-w-none mt-8"
dangerouslySetInnerHTML={{ __html: htmlContent }}
/>
{post.tags.length > 0 && (
<div className="flex gap-2 mt-8">
{post.tags.map((tag) => (
<span
key={tag}
className="bg-gray-100 dark:bg-gray-800 text-gray-700 dark:text-gray-300 text-sm px-3 py-1 rounded-full"
>
#{tag}
</span>
))}
</div>
)}
{relatedPosts.length > 0 && (
<section className="mt-16 pt-8 border-t">
<h2 className="text-2xl font-bold mb-6">📚 مقالات ذات صلة</h2>
<div className="grid md:grid-cols-3 gap-6">
{relatedPosts.map((p) => (
<PostCard key={p.slug} post={p} />
))}
</div>
</section>
)}
</article>
);
}
الخطوة 14: components/ShareButtons.tsx
"use client";
interface ShareButtonsProps {
title: string;
url: string;
}
export default function ShareButtons({ title, url }: ShareButtonsProps) {
const encodedTitle = encodeURIComponent(title);
const encodedUrl = encodeURIComponent(url);
return (
<div className="flex gap-2">
<a
href={`https://twitter.com/intent/tweet?text=${encodedTitle}&url=${encodedUrl}`}
target="_blank"
rel="noopener noreferrer"
className="bg-black text-white px-4 py-2 rounded-lg text-sm hover:bg-gray-800 transition"
>
🐦 تويتر
</a>
<a
href={`https://www.linkedin.com/sharing/share-offsite/?url=${encodedUrl}`}
target="_blank"
rel="noopener noreferrer"
className="bg-blue-700 text-white px-4 py-2 rounded-lg text-sm hover:bg-blue-800 transition"
>
💼 LinkedIn
</a>
<a
href={`https://wa.me/?text=${encodedTitle} - ${encodedUrl}`}
target="_blank"
rel="noopener noreferrer"
className="bg-green-600 text-white px-4 py-2 rounded-lg text-sm hover:bg-green-700 transition"
>
📱 WhatsApp
</a>
</div>
);
}
الخطوة 15: app/categories/[category]/page.tsx
import { notFound } from "next/navigation";
import { getAllCategories, getPostsByCategory } from "@/lib/posts";
import PostCard from "@/components/PostCard";
export function generateStaticParams() {
const categories = getAllCategories();
return categories.map((category) => ({
category: encodeURIComponent(category),
}));
}
export default async function CategoryPage({
params,
}: {
params: Promise<{ category: string }>;
}) {
const { category } = await params;
const decoded = decodeURIComponent(category);
const posts = getPostsByCategory(decoded);
if (posts.length === 0) notFound();
return (
<div className="max-w-6xl mx-auto px-4 py-12">
<h1 className="text-4xl font-bold mb-4">📁 تصنيف: {decoded}</h1>
<p className="text-gray-600 dark:text-gray-400 mb-8">
{posts.length} مقال
</p>
<div className="grid md:grid-cols-2 lg:grid-cols-3 gap-6">
{posts.map((post) => (
<PostCard key={post.slug} post={post} />
))}
</div>
</div>
);
}
الخطوة 16: app/about/page.tsx
export const metadata = {
title: "من نحن",
};
export default function AboutPage() {
return (
<div className="max-w-2xl mx-auto px-4 py-16">
<h1 className="text-4xl font-bold mb-6">من نحن</h1>
<p className="text-lg text-gray-700 dark:text-gray-300 leading-relaxed mb-4">
مرحباً بك في مدونتي! أنا مطور ويب، أشارك هنا مقالاتي عن البرمجة والتقنية.
</p>
<p className="text-lg text-gray-700 dark:text-gray-300 leading-relaxed">
تهدف هذه المدونة إلى نشر المعرفة التقنية بالعربية.
</p>
</div>
);
}
الخطوة 17: app/sitemap.ts
import { getAllPosts } from "@/lib/posts";
import { MetadataRoute } from "next";
export default function sitemap(): MetadataRoute.Sitemap {
const posts = getAllPosts();
const baseUrl = "https://myblog.com";
const postUrls = posts.map((post) => ({
url: `${baseUrl}/blog/${post.slug}`,
lastModified: new Date(post.date),
changeFrequency: "monthly" as const,
priority: 0.8,
}));
return [
{ url: baseUrl, priority: 1 },
{ url: `${baseUrl}/blog`, priority: 0.9 },
{ url: `${baseUrl}/about`, priority: 0.5 },
...postUrls,
];
}
الخطوة 18: تشغيل المشروع
npm run dev
افتح: http://localhost:3000
🎉 مبروك! لديك مدونة احترافية كاملة!
تمارين إضافية
تمرين 1: بحث
أضف ميزة البحث في المقالات.
الحل:
"use client";
import { useState } from "react";
export default function SearchBar({ posts }: { posts: any[] }) {
const [query, setQuery] = useState("");
const filtered = posts.filter((p) =>
p.title.toLowerCase().includes(query.toLowerCase())
);
return (
<div>
<input
value={query}
onChange={(e) => setQuery(e.target.value)}
placeholder="ابحث..."
className="w-full px-4 py-3 border rounded-lg"
/>
<ul>
{filtered.map((p) => (
<li key={p.slug}>{p.title}</li>
))}
</ul>
</div>
);
}
تمرين 2: جدول المحتويات
أضف TOC (Table of Contents).
تمرين 3: الوضع الداكن
أضف زر تبديل الوضع الداكن.
تمرين 4: نشرة بريدية
أضف نموذج الاشتراك.
تمرين 5: تعليقات
أضف نظام تعليقات (Disqus أو Giscus).
تمرين 6: RSS Feed
أضف /feed.xml للمشتركين.
تمرين 7: البحث المتقدم
أضف بحثاً في المحتوى أيضاً.
تمرين 8: النشر
انشر المدونة على Vercel.
قائمة تحقق نهائية
| المهمة | الحالة |
|---|---|
| إنشاء المشروع | ⬜ |
| إعداد Markdown | ⬜ |
| قراءة المقالات | ⬜ |
| الصفحة الرئيسية | ⬜ |
| قائمة المقالات | ⬜ |
| صفحة المقال الفردي | ⬜ |
| SEO و Sitemap | ⬜ |
| النشر | ⬜ |
🎉 مبروك! أكملت مسار Next.js!
لقد وصلت إلى نهاية المسار. أنت الآن تعرف:
- ✅ أساسيات Next.js: App Router، Pages، Layouts.
- ✅ Server Components: Server vs Client.
- ✅ Data Fetching: fetch, cache, Server Actions.
- ✅ API Routes.
- ✅ Styling: Tailwind CSS.
- ✅ Authentication: NextAuth.
- ✅ Deployment: Vercel, Firebase.
- ✅ مشروع متكامل.
ماذا بعد Next.js؟
الآن أنت جاهز لـ:
- بناء مشاريع حقيقية — ابدأ ببناء مشاريعك.
- GraphQL — بديل REST.
- tRPC — Type-safe APIs.
- Prisma — ORM حديث.
- Stripe — الدفع الإلكتروني.
- بناء معرض أعمالك.
الخلاصة
في هذا المشروع، بنيت:
- ✅ مدونة احترافية كاملة.
- ✅ Markdown للمحتوى.
- ✅ SEO محسّن.
- ✅ تصنيفات وبحث.
- ✅ مقالات ذات صلة.
- ✅ مشاركة اجتماعية.
- ✅ نشر سهل.
هذا المشروع هو أساس كل تطبيقات Next.js الاحترافية. احتفظ بالكود، وطور فيه بنفسك!
📚 مقالات ذات صلة
نشر تطبيق Next.js — دليل شامل 2026
دليل عملي مفصل لنشر تطبيق Next.js — Vercel، Firebase، Netlify، والتصدير الثابت
المصادقة في Next.js — دليل شامل 2026
دليل عملي مفصل للمصادقة في Next.js — NextAuth.js، JWT، Middleware، وحماية المسارات
Styling في Next.js — دليل شامل 2026
دليل عملي مفصل لـ Styling في Next.js — Tailwind CSS، CSS Modules، Styled Components، مع تمارين وحلول