Modern Web Development with Next.js 14: The Complete Guide
Modern Web Development with Next.js 14: The Complete Guide
Next.js 14 introduces groundbreaking features that revolutionize React development. This comprehensive guide will help you master the latest version and build modern web applications.
Getting Started
First, create a new Next.js 14 project:
# @filename: script.sh
npx create-next-app@latest my-nextjs-app
cd my-nextjs-app
npm run dev
Key Features in Next.js 14
1. Server Components
Server Components are the default in Next.js 14, offering improved performance and reduced client-side JavaScript:
// @filename: Component.jsx
// app/page.tsx
async function getData() {
const res = await fetch('https://api.example.com/data')
return res.json()
}
const data = await getData()
return (
<main>
<h1>Server Component Example</h1>
<pre>{JSON.stringify(data, null, 2)}</pre>
</main>
)
}
2. App Router
The new App Router provides more intuitive routing and layouts:
// @filename: RootLayout.tsx
// app/layout.tsx
children,
}: {
children: React.ReactNode
}) {
return (
<html lang="en">
<body>
<nav>
<a href="/">Home</a>
<a href="/about">About</a>
</nav>
{children}
</body>
</html>
)
}
// app/about/page.tsx
return (
<div>
<h1>About Us</h1>
<p>Welcome to our website!</p>
</div>
)
}
3. Server Actions
Server Actions enable form handling without API routes:
// @filename: FormPage.tsx
// app/form/page.tsx
async function handleSubmit(formData: FormData) {
'use server'
const name = formData.get('name')
const email = formData.get('email')
// Process form data on the server
await saveToDatabase({ name, email })
}
return (
<form action={handleSubmit}>
<input name="name" placeholder="Name" />
<input name="email" type="email" placeholder="Email" />
<button type="submit">Submit</button>
</form>
)
}
Project: Full-Stack Blog Platform
Let’s build a complete blog platform with Next.js 14:
// @filename: NewPostPage.tsx
// app/types/index.ts
interface Post {
id: string
title: string
content: string
author: string
createdAt: Date
}
// app/lib/db.ts
const prisma = new PrismaClient()
export async function getPosts() {
return prisma.post.findMany({
orderBy: { createdAt: 'desc' }
})
}
export async function createPost(data: Omit<Post, 'id' | 'createdAt'>) {
return prisma.post.create({ data })
}
// app/posts/page.tsx
const posts = await getPosts()
return (
<div className="max-w-4xl mx-auto py-8">
<h1 className="text-3xl font-bold mb-8">Blog Posts</h1>
<div className="space-y-8">
{posts.map((post) => (
<article key={post.id} className="border rounded-lg p-6">
<h2 className="text-2xl font-semibold mb-4">{post.title}</h2>
<p className="text-gray-600 mb-4">{post.content}</p>
<div className="text-sm text-gray-500">
By {post.author} • {post.createdAt.toLocaleDateString()}
</div>
</article>
))}
</div>
</div>
)
}
// app/posts/new/page.tsx
async function createPost(formData: FormData) {
'use server'
const title = formData.get('title') as string
const content = formData.get('content') as string
const author = formData.get('author') as string
await createPost({ title, content, author })
redirect('/posts')
}
return (
<div className="max-w-2xl mx-auto py-8">
<h1 className="text-3xl font-bold mb-8">Create New Post</h1>
<form action={createPost} className="space-y-6">
<div>
<label htmlFor="title" className="block text-sm font-medium">
Title
</label>
<input
type="text"
name="title"
id="title"
className="mt-1 block w-full rounded-md border-gray-300 shadow-sm"
required
/>
</div>
<div>
<label htmlFor="content" className="block text-sm font-medium">
Content
</label>
<textarea
name="content"
id="content"
rows={5}
className="mt-1 block w-full rounded-md border-gray-300 shadow-sm"
required
/>
</div>
<div>
<label htmlFor="author" className="block text-sm font-medium">
Author
</label>
<input
type="text"
name="author"
id="author"
className="mt-1 block w-full rounded-md border-gray-300 shadow-sm"
required
/>
</div>
<button
type="submit"
className="bg-blue-500 text-white px-4 py-2 rounded-md hover:bg-blue-600"
>
Create Post
</button>
</form>
</div>
)
}
Performance Optimization
1. Image Optimization
Next.js provides automatic image optimization:
// @filename: OptimizedImage.tsx
return (
<Image
src="/hero.jpg"
alt="Hero image"
width={1200}
height={600}
priority
className="rounded-lg"
/>
)
}
2. Static Site Generation (SSG)
Generate static pages at build time:
// @filename: Component.jsx
// app/posts/[slug]/page.tsx
export async function generateStaticParams() {
const posts = await getPosts()
return posts.map((post) => ({
slug: post.slug,
}))
}
params,
}: {
params: { slug: string }
}) {
const post = await getPostBySlug(params.slug)
return (
<article>
<h1>{post.title}</h1>
<div>{post.content}</div>
</article>
)
}
3. Incremental Static Regeneration (ISR)
Update static pages without rebuilding:
// @filename: Component.jsx
// app/products/[id]/page.tsx
params,
}: {
params: { id: string }
}) {
const product = await fetch(
`https://api.example.com/products/${params.id}`,
{ next: { revalidate: 3600 } } // Revalidate every hour
).then(res => res.json())
return (
<div>
<h1>{product.name}</h1>
<p>{product.description}</p>
<p>Price: ${product.price}</p>
</div>
)
}
Advanced Features
1. API Routes
Create API endpoints within Next.js:
// @filename: index.ts
// app/api/posts/route.ts
export async function GET() {
const posts = await getPosts()
return NextResponse.json(posts)
}
export async function POST(request: Request) {
const data = await request.json()
const post = await createPost(data)
return NextResponse.json(post, { status: 201 })
}
2. Middleware
Add custom middleware for authentication and more:
// @filename: middleware.tsx
// middleware.ts
export function middleware(request: NextRequest) {
const token = request.cookies.get('token')
if (!token && request.nextUrl.pathname.startsWith('/dashboard')) {
return NextResponse.redirect(new URL('/login', request.url))
}
return NextResponse.next()
}
export const config = {
matcher: '/dashboard/:path*',
}
3. Error Handling
Implement error boundaries and loading states:
// @filename: Error.tsx
// app/error.tsx
'use client'
error,
reset,
}: {
error: Error
reset: () => void
}) {
return (
<div className="flex flex-col items-center justify-center min-h-screen">
<h2 className="text-2xl font-bold mb-4">Something went wrong!</h2>
<p className="text-red-500 mb-4">{error.message}</p>
<button
onClick={reset}
className="bg-blue-500 text-white px-4 py-2 rounded-md"
>
Try again
</button>
</div>
)
}
// app/loading.tsx
return (
<div className="flex items-center justify-center min-h-screen">
<div className="animate-spin rounded-full h-32 w-32 border-t-2 border-b-2 border-blue-500" />
</div>
)
}
Best Practices
-
Project Structure
- Use feature-based organization
- Keep components small and focused
- Implement proper TypeScript types
- Follow consistent naming conventions
-
Performance
- Optimize images and fonts
- Implement proper caching
- Use code splitting
- Monitor Core Web Vitals
-
Security
- Validate user input
- Implement proper authentication
- Use HTTPS
- Follow security headers
-
Development Workflow
- Use ESLint and Prettier
- Write unit tests
- Implement CI/CD
- Document code properly
Conclusion
Next.js 14 provides powerful features for modern web development:
- Server Components for better performance
- App Router for intuitive routing
- Server Actions for simplified form handling
- Built-in optimizations for production
Keep exploring the ecosystem and stay updated with the latest features and best practices.
