Next.js 14+ brings React Server Components, Server Actions, and the App Router — enabling full-stack React apps with minimal client JavaScript.
Getting Started
npx create-next-app@latest my-app --typescript --tailwind --app
cd my-app
npm run dev
Server Components (Default)
// app/posts/page.tsx — runs on server, zero client JS
async function getPosts() {
const res = await fetch("https://api.example.com/posts", { next: { revalidate: 60 } });
return res.json();
}
export default async function PostsPage() {
const posts = await getPosts();
return (
<div>
<h1>Blog Posts</h1>
{posts.map(post => (
<article key={post.id}>
<h2>{post.title}</h2>
<p>{post.excerpt}</p>
</article>
))}
</div>
);
}
Client Components
"use client";
import { useState } from "react";
export function Counter() {
const [count, setCount] = useState(0);
return (
<button onClick={() => setCount(c => c + 1)}>
Count: {count}
</button>
);
}
Server Actions
// app/actions.ts
"use server";
export async function createPost(formData: FormData) {
const title = formData.get("title") as string;
const content = formData.get("content") as string;
await db.post.create({ data: { title, content } });
revalidatePath("/posts");
}
// app/posts/new/page.tsx
import { createPost } from "../actions";
export default function NewPost() {
return (
<form action={createPost}>
<input name="title" required />
<textarea name="content" required />
<button type="submit">Create</button>
</form>
);
}
Dynamic Routes
// app/posts/[slug]/page.tsx
export default async function PostPage({ params }: { params: { slug: string } }) {
const post = await db.post.findUnique({ where: { slug: params.slug } });
if (!post) notFound();
return <article><h1>{post.title}</h1><p>{post.content}</p></article>;
}
export async function generateStaticParams() {
const posts = await db.post.findMany({ select: { slug: true } });
return posts.map(post => ({ slug: post.slug }));
}
Route Handlers (API Routes)
// app/api/posts/route.ts
import { NextResponse } from "next/server";
export async function GET() {
const posts = await db.post.findMany();
return NextResponse.json(posts);
}
export async function POST(request: Request) {
const data = await request.json();
const post = await db.post.create({ data });
return NextResponse.json(post, { status: 201 });
}
Middleware
// middleware.ts
import { NextResponse } from "next/server";
export function middleware(request) {
if (request.nextUrl.pathname.startsWith("/admin")) {
const token = request.cookies.get("auth-token");
if (!token) return NextResponse.redirect(new URL("/login", request.url));
}
return NextResponse.next();
}
export const config = { matcher: ["/admin/:path*"] };
Need to extract or automate web content at scale? Check out my web scraping tools on Apify — no coding required. Or email me at spinov001@gmail.com for custom solutions.
Top comments (0)