Modern Full-Stack Development with Next.js
Exploring the latest features and best practices for building modern web applications.
The Evolution of Next.js
Next.js has revolutionized how we build React applications. From its humble beginnings as a simple server-side rendering framework to the comprehensive full-stack platform it is today, it continues to push the boundaries of what's possible on the web.
Key Features in Modern Next.js
1. App Router
The new App Router introduces a powerful paradigm for building applications:
``typescript
// app/page.tsx
export default function HomePage() {
return (
Welcome to Modern Next.js
)
}
// app/about/page.tsx
export default function AboutPage() {
return
}
`
2. Server Components
Server Components by default for improved performance:
`typescript
// Server Component (default)
async function UserProfile({ userId }: { userId: string }) {
const user = await db.user.findUnique({ where: { id: userId }})
return (
{user.name}
{user.email}
)
}// Client Component with 'use client'
'use client'
import { useState } from 'react'
export function InteractiveButton() {
const [count, setCount] = useState(0)
return (
)
}
`3. Data Fetching
Simplified data fetching with async components:
`typescript
async function BlogPost({ slug }: { slug: string }) {
const post = await fetch(https://api.example.com/posts/${slug})
.then(res => res.json())
return (
{post.title}
{post.content}
)
}
`4. Streaming
Progressive rendering with streaming:
`typescript
import { Suspense } from 'react'async function BlogList() {
const posts = await getPosts()
return posts.map(post => )
}
export default function BlogPage() {
return (
}>
)
}
`Best Practices
1. Component Organization
`typescript
// app/components/ui/button.tsx
export function Button({ children, ...props }: ButtonProps) {
return
}// app/components/features/auth/login-form.tsx
export function LoginForm() {
return (
)
}
`2. Type Safety
Leverage TypeScript throughout:
`typescript
interface User {
id: string
name: string
email: string
}async function getUser(id: string): Promise {
const response = await fetch(
/api/users/${id})
return response.json()
}
`3. API Routes
Build API endpoints with Route Handlers:
`typescript
// app/api/users/route.ts
import { NextResponse } from 'next/server'export async function GET(request: Request) {
const users = await db.user.findMany()
return NextResponse.json(users)
}
export async function POST(request: Request) {
const body = await request.json()
const user = await db.user.create({ data: body })
return NextResponse.json(user, { status: 201 })
}
`Performance Optimization
1. Image Optimization
`typescript
import Image from 'next/image'function ProfilePicture() {
return (
src="/profile.jpg"
alt="Profile"
width={200}
height={200}
priority
/>
)
}
`2. Code Splitting
Automatic code splitting with dynamic imports:
`typescript
import dynamic from 'next/dynamic'const DynamicComponent = dynamic(() => import('./HeavyComponent'), {
loading: () => ,
ssr: false
})
`3. Caching
Leverage Next.js caching:
`typescript
export const revalidate = 3600 // Revalidate every hourasync function Products() {
const products = await fetch('https://api.example.com/products', {
next: { revalidate: 3600 }
}).then(res => res.json())
return
}
`State Management
1. Server State
Use React Query or SWR for server state:
`typescript
'use client'import { useQuery } from '@tanstack/react-query'
function UserProfile({ userId }: { userId: string }) {
const { data: user, isLoading } = useQuery({
queryKey: ['user', userId],
queryFn: () => fetch(
/api/users/${userId}).then(r => r.json())
})
if (isLoading) return
return {user.name}
}
`2. Client State
Use Zustand or Context API for client state:
`typescript
import { create } from 'zustand'const useStore = create((set) => ({
count: 0,
increment: () => set(state => ({ count: state.count + 1 }))
}))
`Styling
1. Tailwind CSS
Modern utility-first CSS:
`typescript
export function Button() {
return (
)
}
`2. CSS Modules
Component-scoped styles:
`typescript
import styles from './button.module.css'export function Button() {
return
}
`Testing
1. Component Testing
`typescript
import { render, screen } from '@testing-library/react'
import { Button } from './button'test('renders button', () => {
render()
expect(screen.getByText('Click me')).toBeInTheDocument()
})
`2. E2E Testing
`typescript
import { test, expect } from '@playwright/test'test('user can login', async ({ page }) => {
await page.goto('/login')
await page.fill('input[name="email"]', 'user@example.com')
await page.fill('input[name="password"]', 'password')
await page.click('button[type="submit"]')
await expect(page).toHaveURL('/dashboard')
})
`Deployment
Vercel Integration
Seamless deployment with Vercel:
`bash
Install Vercel CLI
npm i -g vercelDeploy
vercel
`Environment Variables
`env
.env.local
DATABASE_URL=postgresql://...
NEXT_PUBLIC_API_URL=https://api.example.com
``Conclusion
Next.js provides a comprehensive platform for building modern web applications. By leveraging its features and following best practices, you can build fast, scalable, and maintainable applications.
The key is to understand the tooling, choose the right patterns for your use case, and continuously iterate based on your needs.