S

Server Components

Type-safe Server Components with async data fetching and proper typing for Next.js App Router.

Code

typescript
1// app/users/page.tsx - Server Component (default in App Router)
2interface User {
3 id: number;
4 name: string;
5 email: string;
6 role: "admin" | "user";
7}
8
9interface UsersPageProps {
10 searchParams: Promise<{ page?: string; role?: string }>;
11}
12
13async function getUsers(page: number, role?: string): Promise<User[]> {
14 const params = new URLSearchParams({ page: String(page) });
15 if (role) params.set("role", role);
16
17 const res = await fetch(`https://api.example.com/users?${params}`, {
18 next: { revalidate: 60 }, // ISR: revalidate every 60 seconds
19 });
20
21 if (!res.ok) throw new Error("Failed to fetch users");
22 return res.json();
23}
24
25export default async function UsersPage({ searchParams }: UsersPageProps) {
26 const { page = "1", role } = await searchParams;
27 const users = await getUsers(parseInt(page), role);
28
29 return (
30 <div>
31 <h1>Users</h1>
32 <ul>
33 {users.map((user) => (
34 <li key={user.id}>
35 {user.name} ({user.email}) - {user.role}
36 </li>
37 ))}
38 </ul>
39 </div>
40 );
41}
42
43// app/users/[id]/page.tsx - Dynamic route with params
44interface UserPageProps {
45 params: Promise<{ id: string }>;
46}
47
48async function getUser(id: string): Promise<User> {
49 const res = await fetch(`https://api.example.com/users/${id}`, {
50 cache: "no-store", // Dynamic data
51 });
52 if (!res.ok) throw new Error("User not found");
53 return res.json();
54}
55
56export async function generateMetadata({ params }: UserPageProps) {
57 const { id } = await params;
58 const user = await getUser(id);
59 return {
60 title: user.name,
61 description: `Profile of ${user.name}`,
62 };
63}
64
65export default async function UserPage({ params }: UserPageProps) {
66 const { id } = await params;
67 const user = await getUser(id);
68
69 return (
70 <div>
71 <h1>{user.name}</h1>
72 <p>Email: {user.email}</p>
73 <p>Role: {user.role}</p>
74 </div>
75 );
76}
77
78// Generate static paths
79export async function generateStaticParams(): Promise<{ id: string }[]> {
80 const users = await getUsers(1);
81 return users.map((user) => ({ id: String(user.id) }));
82}

Run this example locally

$ npx create-next-app@latest --typescript