Async Server Components

Make a Server Component async and await your data right inside it β€” no useEffect or loading state needed.

Syntaxexport default async function Page() { const data = await fetch(...); }

Because Server Components run on the server, they can be async and await data directly in the function body. There is no useEffect, no client-side loading flag, and no extra API round trip β€” the data is fetched during rendering and the finished HTML is sent to the browser.

Why this is nice

  • Less code: no effect, no state, no fetch-on-mount.
  • Faster: data is ready before the HTML reaches the user.
  • Secure: fetch logic and secrets stay on the server.

Example

Example Β· typescript
// app/users/page.tsx β€” async Server Component
export default async function Users() {
  const res = await fetch('https://api.example.com/users');
  const users: { id: number; name: string }[] = await res.json();
  return (
    <ul>
      {users.map((u) => (
        <li key={u.id}>{u.name}</li>
      ))}
    </ul>
  );
}

When to use it

  • A news page awaits a CMS API inside the component function, getting data without any client-side effect.
  • A profile page awaits multiple database calls in parallel using Promise.all inside an async Server Component.
  • A pricing page fetches live plan data in the component so it is always fresh without exposing the API key to the browser.

More examples

Awaiting fetch in a page

Marking the component async lets you await any Promise directly without useEffect or useState.

Example Β· ts
// app/news/page.tsx
export default async function NewsPage() {
  const res = await fetch('https://hacker-news.firebaseio.com/v0/topstories.json');
  const ids: number[] = await res.json();
  return <p>Top story ID: {ids[0]}</p>;
}

Parallel data fetching

Promise.all fires both requests simultaneously, halving the wait time compared to sequential awaits.

Example Β· ts
// app/profile/[id]/page.tsx
import { getUser, getOrders } from '@/lib/db';

export default async function ProfilePage({ params }: { params: Promise<{ id: string }> }) {
  const { id } = await params;
  const [user, orders] = await Promise.all([getUser(id), getOrders(id)]);
  return (
    <div>
      <h1>{user.name}</h1>
      <p>{orders.length} orders</p>
    </div>
  );
}

Async layout with session

Layouts can also be async, allowing session checks that apply to every page in a segment.

Example Β· ts
// app/app/layout.tsx
import { getSession } from '@/lib/auth';

export default async function AppLayout({ children }: { children: React.ReactNode }) {
  const session = await getSession();
  return (
    <div>
      <p>Signed in as: {session.email}</p>
      {children}
    </div>
  );
}

Discussion

  • Be the first to comment on this lesson.
Async Server Components β€” Next.js | SoundsCode