// Frontend
Next.js 14 App Router: The Complete Developer's Guide
Master the new App Router in Next.js 14 with server components, streaming, and advanced patterns.
Md Nayeem HossainDec 25, 202412 min read// Frontend
Master the new App Router in Next.js 14 with server components, streaming, and advanced patterns.
Md Nayeem HossainDec 25, 202412 min read
Next.js 14 marks a significant shift in how we build React applications. The introduction of the App Router is not just a routing update; it's a fundamental change in architecture that leverages React Server Components (RSC).
Traditionally, React rendered everything on the client (or hydrated everything). This meant sending a lot of JavaScript to the browser, which could be slow.
Server Components allow us to render components exclusively on the server. The HTML is generated, but no JavaScript is sent to the client for that component. This results in faster initial page loads and smaller bundle sizes.
Here is an example of a Server Component fetching data directly from a database. Notice there is no useEffect or API call—it just runs on the server!
1// app/users/page.tsx - Server Component by default2import { db } from '@/lib/db';3 4async function UsersPage() {5 // Direct database access is safe here because this code ONLY runs on the server6 const users = await db.user.findMany();7 8 return (9 <div>10 <h1>Users List</h1>11 <ul>12 {users.map(user => (13 <li key={user.id}>{user.name}</li>14 ))}15 </ul>16 </div>17 );18}19 20export default UsersPage;You can't do everything on the server. If you need user interaction—like clicking a button, using useState, or listening to window events—you need a Client Component.
You mark a component as a Client Component by adding the "use client" directive at the top of the file.
1"use client"2 3import { useState } from 'react';4 5export default function Counter() {6 // We can use state here because of "use client"7 const [count, setCount] = useState(0);8 9 return (10 <button onClick={() => setCount(count + 1)}>11 Count is {count}12 </button>13 );14}Next.js 14 introduces Server Actions, which allow you to invoke server-side functions directly from client components. This simplifies form handling and data mutations immensely. You often don't even need to create a separate API route anymore.
1// app/actions.ts2"use server"3 4import { db } from '@/lib/db';5import { revalidatePath } from 'next/cache';6 7export async function createUser(formData: FormData) {8 const name = formData.get('name');9 10 await db.user.create({ data: { name } });11 12 // This tells Next.js to refresh the data on the /users page13 revalidatePath('/users');14}Then you can use this action simply as the action prop in a form:
1// app/users/new/page.tsx2import { createUser } from '../actions';3 4export default function NewUserForm() {5 return (6 <form action={createUser}>7 <input name="name" type="text" />8 <button type="submit">Add User</button>9 </form>10 );11}This pattern significantly reduces the amount of "glue code" you need to write to connect your frontend to your backend.