ביג דיזייןBIGDESIGN

Next.js 14 - המדריך המלא בעברית מאפס עד פרודקשן

✍️ארתור קלנדרוב• מייסד BigDesign
📅27 בינואר 2025
⏱️25 דקות קריאה
#Next.js#React#TypeScript#Tutorial#Web Development#JavaScript

נקודות מפתח:

  • ✓התקנה והגדרת סביבת פיתוח
  • ✓App Router - הדרך החדשה לניתוב
  • ✓Server Components vs Client Components
  • ✓Data Fetching ו-Loading States
  • ✓SEO ו-Metadata API
  • ✓פריסה ל-Production עם Vercel

🚀 Next.js 14 - המדריך המלא בעברית

מה נלמד היום?

בניית אפליקציית Full-Stack מודרנית עם Next.js 14

⚡

📋 דרישות מקדימות

לפני שמתחילים, וודאו שיש לכם:

  • ✅ Node.js 18.17 או יותר
  • ✅ ידע בסיסי ב-React
  • ✅ היכרות עם JavaScript/TypeScript
  • ✅ עורך קוד (VSCode מומלץ)

🎯 פרק 1: התקנה והגדרת הפרויקט

שלב 1: יצירת פרויקט חדש

# יצירת פרויקט עם TypeScript ו-Tailwind CSS
npx create-next-app@latest my-app --typescript --tailwind --app

# נכנסים לתיקייה
cd my-app

# מריצים את שרת הפיתוח
npm run dev

שלב 2: מבנה התיקיות

my-app/
├── app/                 # App Router - כל הדפים והלייאאוטים
│   ├── layout.tsx      # Layout ראשי
│   ├── page.tsx        # דף הבית
│   └── globals.css     # סטיילים גלובליים
├── components/         # קומפוננטות
├── lib/               # פונקציות עזר
├── public/            # קבצים סטטיים
└── next.config.js     # הגדרות Next.js

שלב 3: Layout ראשי

// app/layout.tsx
import type { Metadata } from 'next'
import { Inter } from 'next/font/google'
import './globals.css'

const inter = Inter({ subsets: ['latin'] })

export const metadata: Metadata = {
  title: 'האפליקציה שלי',
  description: 'אפליקציית Next.js 14 מדהימה',
}

export default function RootLayout({
  children,
}: {
  children: React.ReactNode
}) {
  return (
    <html lang="he" dir="rtl">
      <body className={inter.className}>
        <nav className="bg-blue-600 text-white p-4">
          <div className="container mx-auto">
            <h1 className="text-xl font-bold">האפליקציה שלי</h1>
          </div>
        </nav>
        <main className="container mx-auto p-4">
          {children}
        </main>
      </body>
    </html>
  )
}

🛣️ פרק 2: App Router - המהפכה של Next.js 14

המבנה החדש

📁 כל תיקייה = Route

מבנה תיקיות URL
app/page.tsx /
app/about/page.tsx /about
app/blog/[slug]/page.tsx /blog/:slug

דוגמה: יצירת דף About

// app/about/page.tsx
export default function AboutPage() {
  return (
    <div className="py-8">
      <h1 className="text-4xl font-bold mb-4">אודות</h1>
      <p className="text-lg text-gray-700">
        זהו דף האודות של האפליקציה שלנו
      </p>
    </div>
  )
}

Dynamic Routes - ניתוב דינמי

// app/products/[id]/page.tsx
interface Props {
  params: { id: string }
}

export default async function ProductPage({ params }: Props) {
  const product = await getProduct(params.id)
  
  return (
    <div>
      <h1>{product.name}</h1>
      <p>מחיר: ₪{product.price}</p>
    </div>
  )
}

// פונקציה לקבלת נתונים
async function getProduct(id: string) {
  const res = await fetch(`https://api.example.com/products/${id}`)
  return res.json()
}

⚛️ פרק 3: Server Components vs Client Components

ההבדל הקריטי

🖥️ Server Components

  • ✅ ברירת מחדל ב-Next.js 14
  • ✅ רצים בצד השרת
  • ✅ יכולים לגשת ל-DB ישירות
  • ✅ אין JavaScript בצד הלקוח
  • ✅ SEO מעולה
  • ❌ אין useState/useEffect
  • ❌ אין event handlers
<div class="client-component bg-blue-50 p-6 rounded-xl">
  <h3 class="text-xl font-bold text-blue-800 mb-3">💻 Client Components</h3>
  <ul class="space-y-2 text-sm">
    <li>✅ מוסיפים 'use client' בראש הקובץ</li>
    <li>✅ רצים בדפדפן</li>
    <li>✅ יכולים להשתמש ב-hooks</li>
    <li>✅ אינטראקטיביות מלאה</li>
    <li>✅ Browser APIs</li>
    <li>❌ לא יכולים לגשת ל-DB</li>
    <li>❌ Bundle size גדול יותר</li>
  </ul>
</div>

דוגמת Server Component

// app/posts/page.tsx
// 🖥️ Server Component - אין צורך ב-'use client'

async function getPosts() {
  const res = await fetch('https://api.example.com/posts', {
    cache: 'no-store' // תמיד נתונים טריים
  })
  return res.json()
}

export default async function PostsPage() {
  const posts = await getPosts()
  
  return (
    <div className="grid gap-4">
      {posts.map((post: any) => (
        <article key={post.id} className="p-4 border rounded">
          <h2 className="text-xl font-bold">{post.title}</h2>
          <p className="text-gray-600">{post.excerpt}</p>
        </article>
      ))}
    </div>
  )
}

דוגמת Client Component

// components/Counter.tsx
'use client' // 👈 הופך את הקומפוננטה ל-Client Component

import { useState } from 'react'

export default function Counter() {
  const [count, setCount] = useState(0)
  
  return (
    <div className="flex items-center gap-4">
      <button 
        onClick={() => setCount(count - 1)}
        className="px-4 py-2 bg-red-500 text-white rounded"
      >
        -
      </button>
      <span className="text-2xl font-bold">{count}</span>
      <button 
        onClick={() => setCount(count + 1)}
        className="px-4 py-2 bg-green-500 text-white rounded"
      >
        +
      </button>
    </div>
  )
}

📊 פרק 4: Data Fetching - קבלת נתונים

1. Fetching בצד השרת

// app/users/page.tsx
interface User {
  slug: number
  name: string
  email: string
}

async function getUsers(): Promise<User[]> {
  const res = await fetch('https://jsonplaceholder.typicode.com/users', {
    next: { revalidate: 3600 } // Cache ל-1 שעה
  })
  
  if (!res.ok) {
    throw new Error('Failed to fetch users')
  }
  
  return res.json()
}

export default async function UsersPage() {
  const users = await getUsers()
  
  return (
    <div>
      <h1 className="text-3xl font-bold mb-6">משתמשים</h1>
      <div className="grid gap-4 md:grid-cols-2 lg:grid-cols-3">
        {users.map(user => (
          <div key={user.id} className="p-4 border rounded-lg">
            <h2 className="font-bold">{user.name}</h2>
            <p className="text-gray-600">{user.email}</p>
          </div>
        ))}
      </div>
    </div>
  )
}

2. Loading States

// app/users/loading.tsx
export default function Loading() {
  return (
    <div className="flex justify-center items-center min-h-[400px]">
      <div className="animate-spin rounded-full h-12 w-12 border-b-2 border-blue-600"></div>
    </div>
  )
}

3. Error Handling

// app/users/error.tsx
'use client'

export default function Error({
  error,
  reset,
}: {
  error: Error & { digest?: string }
  reset: () => void
}) {
  return (
    <div className="text-center py-10">
      <h2 className="text-2xl font-bold text-red-600 mb-4">
        משהו השתבש!
      </h2>
      <p className="text-gray-600 mb-4">{error.message}</p>
      <button
        onClick={() => reset()}
        className="px-6 py-2 bg-blue-600 text-white rounded"
      >
        נסה שוב
      </button>
    </div>
  )
}

🔍 פרק 5: SEO ו-Metadata

Static Metadata

// app/blog/page.tsx
import type { Metadata } from 'next'

export const metadata: Metadata = {
  title: 'הבלוג שלי | מאמרים מעניינים',
  description: 'קראו את המאמרים האחרונים בבלוג שלנו',
  openGraph: {
    title: 'הבלוג שלי',
    description: 'מאמרים על פיתוח ועיצוב אתרים',
    images: ['/og-image.jpg'],
  },
  twitter: {
    card: 'summary_large_image',
    title: 'הבלוג שלי',
    description: 'מאמרים על פיתוח ועיצוב אתרים',
    images: ['/twitter-image.jpg'],
  },
}

export default function BlogPage() {
  return <div>הבלוג שלי</div>
}

Dynamic Metadata

// app/blog/[slug]/page.tsx
import type { Metadata } from 'next'

interface Props {
  params: { slug: string }
}

export async function generateMetadata({ params }: Props): Promise<Metadata> {
  const post = await getPost(params.slug)
  
  return {
    title: post.title,
    description: post.excerpt,
    openGraph: {
      title: post.title,
      description: post.excerpt,
      images: [post.image],
      type: 'article',
      publishedTime: post.date,
      authors: [post.author],
    },
  }
}

export default async function PostPage({ params }: Props) {
  const post = await getPost(params.slug)
  
  return (
    <article>
      <h1>{post.title}</h1>
      <div dangerouslySetInnerHTML={{ __html: post.content }} />
    </article>
  )
}

🎨 פרק 6: Styling - עיצוב האפליקציה

Tailwind CSS

// components/Card.tsx
export default function Card({ children }: { children: React.ReactNode }) {
  return (
    <div className="
      bg-white 
      rounded-lg 
      shadow-lg 
      p-6 
      hover:shadow-xl 
      transition-shadow 
      duration-300
    ">
      {children}
    </div>
  )
}

CSS Modules

// components/Button.module.css
.button {
  padding: 0.75rem 1.5rem;
  border-radius: 0.5rem;
  font-weight: 600;
  transition: all 0.3s;
  
  &.primary {
    background: #3b82f6;
    color: white;
    
    &:hover {
      background: #2563eb;
    }
  }
  
  &.secondary {
    background: #f3f4f6;
    color: #1f2937;
    
    &:hover {
      background: #e5e7eb;
    }
  }
}
// components/Button.tsx
import styles from './Button.module.css'

interface ButtonProps {
  variant?: 'primary' | 'secondary'
  children: React.ReactNode
  onClick?: () => void
}

export default function Button({ 
  variant = 'primary', 
  children, 
  onClick 
}: ButtonProps) {
  return (
    <button 
      className={`${styles.button} ${styles[variant]}`}
      onClick={onClick}
    >
      {children}
    </button>
  )
}

🔒 פרק 7: API Routes

יצירת API Endpoint

// app/api/posts/route.ts
import { NextRequest, NextResponse } from 'next/server'

// GET /api/posts
export async function GET(request: NextRequest) {
  const posts = await getPosts()
  
  return NextResponse.json(posts)
}

// POST /api/posts
export async function POST(request: NextRequest) {
  const body = await request.json()
  
  // Validation
  if (!body.title || !body.content) {
    return NextResponse.json(
      { error: 'Title and content are required' },
      { status: 400 }
    )
  }
  
  const newPost = await createPost(body)
  
  return NextResponse.json(newPost, { status: 201 })
}

Dynamic API Routes

// app/api/posts/[id]/route.ts
interface Context {
  params: { id: string }
}

// GET /api/posts/:id
export async function GET(request: NextRequest, context: Context) {
  const post = await getPost(context.params.id)
  
  if (!post) {
    return NextResponse.json(
      { error: 'Post not found' },
      { status: 404 }
    )
  }
  
  return NextResponse.json(post)
}

// PUT /api/posts/:id
export async function PUT(request: NextRequest, context: Context) {
  const body = await request.json()
  const updatedPost = await updatePost(context.params.id, body)
  
  return NextResponse.json(updatedPost)
}

// DELETE /api/posts/:id
export async function DELETE(request: NextRequest, context: Context) {
  await deletePost(context.params.id)
  
  return NextResponse.json({ success: true })
}

🚀 פרק 8: פריסה ל-Production

1. אופטימיזציה לפני פריסה

# בדיקת build
npm run build

# הרצה במצב production מקומי
npm run start

2. פריסה ל-Vercel

📦 שלבי הפריסה

  1. 1

    Push ל-GitHub

    git push origin main
  2. <li class="flex items-start">
      <span class="bg-blue-600 text-white rounded-full w-8 h-8 flex items-center justify-center font-bold mr-3">2</span>
      <div>
        <h4 class="font-bold">התחברות ל-Vercel</h4>
        <p>היכנסו ל-vercel.com וחברו את ה-repo</p>
      </div>
    </li>
    
    <li class="flex items-start">
      <span class="bg-blue-600 text-white rounded-full w-8 h-8 flex items-center justify-center font-bold mr-3">3</span>
      <div>
        <h4 class="font-bold">הגדרות Environment</h4>
        <p>הוסיפו משתני סביבה אם נדרש</p>
      </div>
    </li>
    
    <li class="flex items-start">
      <span class="bg-blue-600 text-white rounded-full w-8 h-8 flex items-center justify-center font-bold mr-3">4</span>
      <div>
        <h4 class="font-bold">Deploy!</h4>
        <p>לחצו Deploy והאתר שלכם באוויר!</p>
      </div>
    </li>
    

3. Environment Variables

# .env.local
DATABASE_URL=postgresql://...
NEXT_PUBLIC_API_URL=https://api.example.com
SECRET_KEY=your-secret-key
// שימוש במשתני סביבה
const apiUrl = process.env.NEXT_PUBLIC_API_URL // זמין בצד הלקוח
const dbUrl = process.env.DATABASE_URL // רק בצד השרת

💡 טיפים ו-Best Practices

✅ כן לעשות

  • • השתמשו ב-Server Components כברירת מחדל
  • • נצלו את ה-caching של Next.js
  • • השתמשו ב-Image component לתמונות
  • • הוסיפו loading.tsx לכל route
  • • השתמשו ב-TypeScript

❌ לא לעשות

  • • אל תשתמשו ב-'use client' ללא צורך
  • • אל תעשו fetch בצד הלקוח אם אפשר בשרת
  • • אל תשכחו error boundaries
  • • אל תזניחו SEO
  • • אל תטענו פונטים מ-Google Fonts ישירות

🎯 פרויקט לתרגול

בנו בלוג אישי עם:

  1. ✅ דף בית עם רשימת פוסטים
  2. ✅ דף פוסט בודד עם Markdown
  3. ✅ מערכת תגובות (Client Component)
  4. ✅ חיפוש פוסטים
  5. ✅ Dark mode toggle
  6. ✅ RSS feed
  7. ✅ Sitemap אוטומטי

קוד התחלתי:

// app/page.tsx
import Link from 'next/link'
import { getPosts } from '@/lib/posts'

export default async function HomePage() {
  const posts = await getPosts()
  
  return (
    <div className="max-w-4xl mx-auto py-8">
      <h1 className="text-4xl font-bold mb-8">הבלוג שלי</h1>
      
      <div className="grid gap-6">
        {posts.map(post => (
          <article key={post.slug} className="border-b pb-6">
            <h2 className="text-2xl font-bold mb-2">
              <Link href={`/blog/${post.slug}`} className="hover:text-blue-600">
                {post.title}
              </Link>
            </h2>
            <p className="text-gray-600 mb-2">{post.date}</p>
            <p className="text-gray-700">{post.excerpt}</p>
          </article>
        ))}
      </div>
    </div>
  )
}

📚 משאבים נוספים

🔗 לינקים שימושיים

תיעוד רשמי

<div>
  <h4 class="font-bold mb-3">כלים מומלצים</h4>
  <ul class="space-y-2">
    <li>🚀 <a href="https://vercel.com" class="text-blue-600 hover:underline">Vercel - Hosting</a></li>
    <li>🗄️ <a href="https://supabase.com" class="text-blue-600 hover:underline">Supabase - Database</a></li>
    <li>🔐 <a href="https://clerk.com" class="text-blue-600 hover:underline">Clerk - Authentication</a></li>
    <li>💳 <a href="https://stripe.com" class="text-blue-600 hover:underline">Stripe - Payments</a></li>
  </ul>
</div>

🎉 סיכום

🏆 מה למדנו היום?

🎯

App Router

הדרך החדשה לניתוב ב-Next.js

<div class="achievement bg-white p-4 rounded-lg">
  <div class="text-3xl mb-2">⚛️</div>
  <h4 class="font-bold">Server Components</h4>
  <p class="text-sm text-gray-600">קומפוננטות שרצות בשרת</p>
</div>

<div class="achievement bg-white p-4 rounded-lg">
  <div class="text-3xl mb-2">🚀</div>
  <h4 class="font-bold">Production Ready</h4>
  <p class="text-sm text-gray-600">פריסה מהירה ל-Vercel</p>
</div>

Next.js 14 היא הפלטפורמה המושלמת לבניית אפליקציות React מודרניות.
עם Server Components, App Router, ו-Edge Runtime,
אתם יכולים לבנות אפליקציות מהירות, מאובטחות ומותאמות SEO.

🚀 הצעד הבא?

מוכנים לבנות את האפליקציה הבאה שלכם עם Next.js 14?

בואו נבנה ביחד →
ארתור קלנדרוב

ארתור קלנדרוב

מייסד BigDesign

"Next.js 14 שינה את הדרך שבה אנחנו בונים אפליקציות. זה הזמן להצטרף למהפכה!"

מעוניינים לקבל ייעוץ מקצועי?

צוות BigDesign כאן בשבילכם. נשמח לעזור לכם לבנות את האתר המושלם לעסק שלכם.