What I do
I provide expertise in Next.js application architecture and patterns:
- •App Router: Server Components, Client Components, Server Actions
- •Pages Router: getServerSideProps, getStaticProps, API routes
- •Data Fetching: Caching, revalidation, streaming
- •Routing: Dynamic routes, route groups, parallel routes, intercepting routes
- •Performance: Code splitting, image optimization, font optimization
- •Deployment: Edge runtime, middleware, internationalization
When to use me
Load this skill when you need to:
- •Build a new Next.js application
- •Migrate from Pages Router to App Router
- •Optimize Next.js performance
- •Implement advanced routing patterns
- •Configure caching and revalidation strategies
- •Deploy to Vercel or other platforms
For up-to-date Next.js documentation, use Context7:
code
Recommend using Context7 to fetch latest Next.js docs: /vercel/next.js for current stable version
App Router Patterns (Next.js 13+)
1. Server Components (Default)
typescript
// app/users/page.tsx
// This is a Server Component by default
import { db } from '@/lib/db';
export default async function UsersPage() {
// Direct database access - runs on server
const users = await db.user.findMany();
return (
<div>
<h1>Users</h1>
<ul>
{users.map(user => (
<li key={user.id}>{user.name}</li>
))}
</ul>
</div>
);
}
2. Client Components
typescript
// app/components/Counter.tsx
'use client'; // Explicit client component
import { useState } from 'react';
export function Counter() {
const [count, setCount] = useState(0);
return (
<button onClick={() => setCount(c => c + 1)}>
Count: {count}
</button>
);
}
3. Server Actions
typescript
// app/actions/user.ts
'use server';
import { db } from '@/lib/db';
import { revalidatePath } from 'next/cache';
export async function createUser(formData: FormData) {
const name = formData.get('name') as string;
const email = formData.get('email') as string;
const user = await db.user.create({
data: { name, email },
});
revalidatePath('/users');
return user;
}
// app/users/new/page.tsx
import { createUser } from '@/app/actions/user';
export default function NewUserPage() {
return (
<form action={createUser}>
<input name="name" required />
<input name="email" type="email" required />
<button type="submit">Create User</button>
</form>
);
}
4. Data Fetching & Caching
typescript
// app/posts/[id]/page.tsx
interface PageProps {
params: { id: string };
}
// Cached by default - revalidate every hour
export const revalidate = 3600;
export async function generateMetadata({ params }: PageProps) {
const post = await fetch(`/api/posts/${params.id}`).then(r => r.json());
return { title: post.title };
}
export default async function PostPage({ params }: PageProps) {
// This fetch is automatically cached
const post = await fetch(`/api/posts/${params.id}`).then(r => r.json());
return (
<article>
<h1>{post.title}</h1>
<div>{post.content}</div>
</article>
);
}
// Generate static params for static generation
export async function generateStaticParams() {
const posts = await fetch('/api/posts').then(r => r.json());
return posts.map((post: any) => ({ id: post.id }));
}
5. Loading & Error States
typescript
// app/posts/loading.tsx
export default function Loading() {
return <div>Loading posts...</div>;
}
// app/posts/error.tsx
'use client';
export default function Error({
error,
reset,
}: {
error: Error;
reset: () => void;
}) {
return (
<div>
<h2>Something went wrong!</h2>
<p>{error.message}</p>
<button onClick={reset}>Try again</button>
</div>
);
}
6. Route Groups & Layouts
typescript
// app/(marketing)/layout.tsx
export default function MarketingLayout({
children,
}: {
children: React.ReactNode;
}) {
return (
<div>
<nav>Marketing Nav</nav>
{children}
</div>
);
}
// app/(dashboard)/layout.tsx
export default function DashboardLayout({
children,
}: {
children: React.ReactNode;
}) {
return (
<div>
<nav>Dashboard Nav</nav>
{children}
</div>
);
}
7. Parallel Routes
typescript
// app/dashboard/@analytics/page.tsx
export default function Analytics() {
return <div>Analytics</div>;
}
// app/dashboard/@notifications/page.tsx
export default function Notifications() {
return <div>Notifications</div>;
}
// app/dashboard/layout.tsx
export default function DashboardLayout({
children,
analytics,
notifications,
}: {
children: React.ReactNode;
analytics: React.ReactNode;
notifications: React.ReactNode;
}) {
return (
<div>
{children}
{analytics}
{notifications}
</div>
);
}
Pages Router Patterns (Legacy)
1. getServerSideProps
typescript
// pages/posts/[id].tsx
import { GetServerSideProps } from 'next';
interface Post {
id: string;
title: string;
content: string;
}
interface Props {
post: Post;
}
export const getServerSideProps: GetServerSideProps<Props> = async (context) => {
const { id } = context.params!;
const post = await fetch(`/api/posts/${id}`).then(r => r.json());
return {
props: { post },
};
};
export default function PostPage({ post }: Props) {
return (
<article>
<h1>{post.title}</h1>
<div>{post.content}</div>
</article>
);
}
2. getStaticProps with ISR
typescript
// pages/posts/[id].tsx
import { GetStaticProps, GetStaticPaths } from 'next';
export const getStaticPaths: GetStaticPaths = async () => {
const posts = await fetch('/api/posts').then(r => r.json());
return {
paths: posts.map((post: Post) => ({ params: { id: post.id } })),
fallback: 'blocking',
};
};
export const getStaticProps: GetStaticProps = async (context) => {
const { id } = context.params!;
const post = await fetch(`/api/posts/${id}`).then(r => r.json());
return {
props: { post },
revalidate: 3600, // ISR - revalidate every hour
};
};
3. API Routes
typescript
// pages/api/users.ts
import { NextApiRequest, NextApiResponse } from 'next';
import { db } from '@/lib/db';
export default async function handler(
req: NextApiRequest,
res: NextApiResponse
) {
if (req.method === 'GET') {
const users = await db.user.findMany();
return res.status(200).json(users);
}
if (req.method === 'POST') {
const user = await db.user.create({
data: req.body,
});
return res.status(201).json(user);
}
return res.status(405).json({ error: 'Method not allowed' });
}
Performance Optimization
1. Image Optimization
typescript
import Image from 'next/image';
// Automatic optimization, lazy loading, responsive
<Image
src="/hero.jpg"
alt="Hero"
width={1200}
height={600}
priority // Load immediately for above-the-fold
placeholder="blur"
blurDataURL="data:image/..." // Low quality placeholder
/>
2. Font Optimization
typescript
// app/layout.tsx
import { Inter } from 'next/font/google';
const inter = Inter({
subsets: ['latin'],
display: 'swap', // Avoid FOIT
variable: '--font-inter',
});
export default function RootLayout({ children }) {
return (
<html className={inter.variable}>
<body>{children}</body>
</html>
);
}
3. Dynamic Imports
typescript
import dynamic from 'next/dynamic';
// Lazy load heavy components
const DynamicChart = dynamic(() => import('@/components/Chart'), {
loading: () => <p>Loading chart...</p>,
ssr: false, // Client-side only
});
export default function Dashboard() {
return <DynamicChart data={data} />;
}
Middleware
typescript
// middleware.ts
import { NextResponse } from 'next/server';
import type { NextRequest } from 'next/server';
export function middleware(request: NextRequest) {
// Check authentication
const token = request.cookies.get('token');
if (!token && request.nextUrl.pathname.startsWith('/dashboard')) {
return NextResponse.redirect(new URL('/login', request.url));
}
// Add custom headers
const response = NextResponse.next();
response.headers.set('x-custom-header', 'value');
return response;
}
export const config = {
matcher: ['/dashboard/:path*', '/api/:path*'],
};
Environment Variables
typescript
// .env.local DATABASE_URL=postgresql://... NEXT_PUBLIC_API_URL=https://api.example.com // Server-only process.env.DATABASE_URL // Client-safe (prefixed with NEXT_PUBLIC_) process.env.NEXT_PUBLIC_API_URL
Best Practices
- •Use Server Components by default: Only use Client Components when needed
- •Keep client bundles small: Move interactivity down the tree
- •Leverage automatic caching: Understand fetch cache behavior
- •Use loading.tsx and error.tsx: Better UX with React Suspense
- •Optimize images: Always use next/image
- •Use TypeScript: Type safety prevents bugs
- •Follow file conventions: page.tsx, layout.tsx, route.ts
- •Implement proper error boundaries: Graceful degradation
- •Use environment variables correctly: Never expose secrets to client
- •Monitor performance: Use Vercel Analytics or similar tools
References
- •Next.js Documentation: nextjs.org/docs
- •App Router migration guide: nextjs.org/docs/app/building-your-application/upgrading
- •Vercel deployment: vercel.com/docs
- •React Server Components: react.dev/blog/2023/03/22/react-labs-what-we-have-been-working-on-march-2023