Building Modern Web Applications with Next.js and TypeScript
Explore the power of Next.js 15 and TypeScript for creating scalable, performant web applications. Learn best practices and advanced patterns.
Building Modern Web Applications with Next.js and TypeScript
In today's rapidly evolving web development landscape, choosing the right stack is crucial for building scalable and maintainable applications. Next.js 15 combined with TypeScript provides an excellent foundation for modern web development.
Why Next.js?
Next.js has revolutionized React development by providing:
- Server-Side Rendering (SSR) for better SEO and performance
- Static Site Generation (SSG) for lightning-fast sites
- API Routes for full-stack development
- Automatic Code Splitting for optimized loading
- Built-in CSS and Sass Support
TypeScript Benefits
TypeScript adds powerful type safety to JavaScript:
interface User {
id: string;
name: string;
email: string;
role: "admin" | "user";
}
const createUser = (userData: Omit<User, "id">): User => {
return {
id: generateId(),
...userData,
};
};
Best Practices
1. Project Structure
Organize your project with clear separation of concerns:
src/
├── components/
├── pages/
├── utils/
├── types/
└── styles/
2. Type Safety
Always define proper interfaces and use TypeScript's strict mode:
// tsconfig.json
{
"compilerOptions": {
"strict": true,
"noImplicitAny": true,
"strictNullChecks": true
}
}
3. Performance Optimization
- Use Next.js Image optimization
- Implement proper caching strategies
- Leverage React.memo for component optimization
Advanced Patterns
Custom Hooks
Create reusable logic with custom hooks:
import { useState, useEffect } from 'react';
const useLocalStorage = <T>(key: string, initialValue: T) => {
const [value, setValue] = useState<T>(initialValue);
useEffect(() => {
try {
const item = localStorage.getItem(key);
if (item) {
setValue(JSON.parse(item));
}
} catch (error) {
console.error(`Error reading localStorage key "${key}":`, error);
}
}, [key]);
const setStoredValue = (newValue: T | ((val: T) => T)) => {
try {
const valueToStore = newValue instanceof Function ? newValue(value) : newValue;
setValue(valueToStore);
localStorage.setItem(key, JSON.stringify(valueToStore));
} catch (error) {
console.error(`Error setting localStorage key "${key}":`, error);
}
};
return [value, setStoredValue] as const;
};
// Usage example
function UserProfile() {
const [user, setUser] = useLocalStorage('user', { name: '', email: '' });
return (
<form>
<input
value={user.name}
onChange={(e) => setUser(prev => ({ ...prev, name: e.target.value }))}
placeholder="Name"
/>
<input
value={user.email}
onChange={(e) => setUser(prev => ({ ...prev, email: e.target.value }))}
placeholder="Email"
/>
</form>
);
}
API Routes with Validation
// app/api/users/route.ts
import { NextRequest, NextResponse } from "next/server";
import { z } from "zod";
const CreateUserSchema = z.object({
name: z.string().min(1, "Name is required"),
email: z.string().email("Invalid email format"),
age: z.number().min(13, "Must be at least 13 years old"),
});
export async function POST(request: NextRequest) {
try {
const body = await request.json();
const validatedData = CreateUserSchema.parse(body);
// Create user logic here
const user = await createUser(validatedData);
return NextResponse.json(
{
success: true,
user,
},
{ status: 201 }
);
} catch (error) {
if (error instanceof z.ZodError) {
return NextResponse.json(
{
success: false,
errors: error.errors,
},
{ status: 400 }
);
}
return NextResponse.json(
{
success: false,
message: "Internal server error",
},
{ status: 500 }
);
}
}
Conclusion
Next.js and TypeScript provide a robust foundation for building modern web applications. The combination offers excellent developer experience, type safety, and performance optimization out of the box.
Start your next project with this powerful stack and experience the difference in development productivity and application quality.