Next.js Data Fetching

Fetch data in Server Components, run requests in parallel, and load data on the client when needed.

TL;DR

  1. Fetch data with await inside an async Server Component.
  2. Start independent requests together with Promise.all() to save time.
  3. Opt in to caching with cache: 'force-cache' or 'use cache'.

Fetching on the Server

    fetch()

    Await a request inside an async Server Component and render the result.

    const API = 'https://api.example.com';
    
    export default async function Page() {
      const res = await fetch(`${API}/posts`);
      const posts = await res.json();
      return <p>{posts.length} posts</p>;
    }
    Database Query

    Query the database directly, with no API route in between.

    import { sql } from '@/lib/db';
    
    export default async function Page() {
      const posts = await sql`SELECT * FROM posts`;
      return <p>{posts.length} posts</p>;
    }
    res.ok

    Check the response and throw, so the nearest error.tsx shows a fallback.

    const res = await fetch(`${API}/posts`);
    if (!res.ok) {
      throw new Error('Failed to load posts');
    }
    const posts = await res.json();

Avoiding Waterfalls

    Promise.all()

    Run independent requests at the same time instead of one after another.

    const [user, orders] = await Promise.all([
      getUser(id),
      getOrders(id),
    ]);
    Preload

    Start a request early without awaiting it, then await it where it is needed.

    const ordersPromise = getOrders(id); // starts now
    const user = await getUser(id);
    const orders = await ordersPromise;
    <Suspense>

    Let a slow component load on its own without blocking the rest of the page.

    <h1>Dashboard</h1>
    <Suspense fallback={<p>Loading stats...</p>}>
      <SlowStats />
    </Suspense>

Caching Results

    force-cache

    Cache one request so its result is reused across users and requests.

    const res = await fetch(`${API}/posts`, {
      cache: 'force-cache',
    });
    next.revalidate

    Refresh a cached request in the background after a set number of seconds.

    const res = await fetch(`${API}/posts`, {
      next: { revalidate: 3600 },
    });
    'use cache'

    Cache any async function, including database queries, when Cache Components is enabled.

    export async function getPosts() {
      'use cache';
      return sql`SELECT * FROM posts`;
    }

Client and URL-Driven Data

    use()

    Read a promise passed from a Server Component inside a Suspense boundary.

    'use client';
    import { use } from 'react';
    
    export function Posts({
      data,
    }: {
      data: Promise<Post[]>;
    }) {
      const posts = use(data);
      return <p>{posts.length} posts</p>;
    }
    useSWR()

    Fetch and refresh on the client for data that changes often.

    'use client';
    import useSWR from 'swr';
    
    const fetcher = (url: string) =>
      fetch(url).then((r) => r.json());
    
    export function Profile() {
      const { data } = useSWR('/api/user', fetcher);
      return <p>{data?.name}</p>;
    }
    searchParams

    Refetch on the server whenever a filter in the URL changes.

    export default async function Page({
      searchParams,
    }: {
      searchParams: Promise<{ q?: string }>;
    }) {
      const { q = '' } = await searchParams;
      const items = await search(q);
      return <List items={items} />;
    }

Tips

  1. Fetch data in the component that needs it rather than passing it down, since identical fetch calls are deduplicated in one render.
  2. Wrap slow sections in <Suspense>, so the rest of the page appears while that data is still loading.

Warnings

  1. A fetch call is not cached by default in current Next.js, so add cache: 'force-cache' when the data rarely changes.
  2. Awaiting requests one after another creates a waterfall, so use Promise.all() when the requests do not depend on each other.

In Practice

FAQ