Next.js📅 2026-11-20⏱ 13 دقائق قراءة⚡ مقال 10 من 10

مشروع 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-htmlPlugin لـ 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؟

الآن أنت جاهز لـ:

  1. بناء مشاريع حقيقية — ابدأ ببناء مشاريعك.
  2. GraphQL — بديل REST.
  3. tRPC — Type-safe APIs.
  4. Prisma — ORM حديث.
  5. Stripe — الدفع الإلكتروني.
  6. بناء معرض أعمالك.

الخلاصة

في هذا المشروع، بنيت:

  • ✅ مدونة احترافية كاملة.
  • ✅ Markdown للمحتوى.
  • ✅ SEO محسّن.
  • ✅ تصنيفات وبحث.
  • ✅ مقالات ذات صلة.
  • ✅ مشاركة اجتماعية.
  • ✅ نشر سهل.

هذا المشروع هو أساس كل تطبيقات Next.js الاحترافية. احتفظ بالكود، وطور فيه بنفسك!

📚 مقالات ذات صلة

Next.js

نشر تطبيق Next.js — دليل شامل 2026

دليل عملي مفصل لنشر تطبيق Next.js — Vercel، Firebase، Netlify، والتصدير الثابت

Next.js

المصادقة في Next.js — دليل شامل 2026

دليل عملي مفصل للمصادقة في Next.js — NextAuth.js، JWT، Middleware، وحماية المسارات

Next.js

Styling في Next.js — دليل شامل 2026

دليل عملي مفصل لـ Styling في Next.js — Tailwind CSS، CSS Modules، Styled Components، مع تمارين وحلول