🚀 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
Push ל-GitHub
git push origin main
<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 ישירות
🎯 פרויקט לתרגול
בנו בלוג אישי עם:
- ✅ דף בית עם רשימת פוסטים
- ✅ דף פוסט בודד עם Markdown
- ✅ מערכת תגובות (Client Component)
- ✅ חיפוש פוסטים
- ✅ Dark mode toggle
- ✅ RSS feed
- ✅ 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.