Ben
Ben

Reputation: 16669

Next.js data fetching using Suspense and ErrorBoundary

I am trying to wrap my head around how to conveniently work with data fetching in Next.js 15 (app router) using Suspense and ErrorBoundary.

Below please find my current approach. This works fine. But it seems like a lot of boilerplate to write. In particular, the data fetching has to occur in a child component of the suspense boundary.

Is this how people normally do this? I'm assuming it's not intended to fetch the data in page.tsx itself?

page.tsx

export default async function ArticleListPage() {
    return (
        <main>
            <h1>Articles</h1>
            <ErrorBoundary fallback= {<> Error: Unable to load articles.</>}>
                <Suspense fallback={<>Loading...</>}>
                    <ArticleList />
                </Suspense>
            </ErrorBoundary>
        </main>
    );
}

ArticleList.tsx

export async function ArticleList() {

    const articles = await fetchAllArticles();

    return (
        <>
            { articles.map((article: any) => (
                <Link
                    key={article.slug}
                    href={`/articles/${article.slug}`}
                >
                    <img src={article.cover} />
                    <h2>{article.title}</h2>
                    <p>{article.description}</p>
                </Link>
            ))}
        </>
    );
}

Upvotes: 1

Views: 312

Answers (1)

Bill.zhanxg
Bill.zhanxg

Reputation: 63

The code you provided is correct for your case. Alternatively, you can use loading.tsx and error.tsx on the same route to server the page instantly and catch error in page.tsx. The downside is that the entire page will be replaced with loading.tsx therefore you need to add and in the loading.tsx to make a loading skeleton that matches the page itself (or you can just use a spinner). This design is intentional because next can serve the static part straight away and wait for the Suspense part to stream in to decrease TTFB (Time to First Byte) and FP (First Paint) time.

Here are the docs to learn more:

You can also use Partial Prerendering (PPR, it's still experimental) to improve fetching even more: https://nextjs.org/docs/app/building-your-application/rendering/partial-prerendering

Here is an example:

page.tsx

export default async function ArticleListPage() {
    const articles = await fetchAllArticles();
    return (
        <main>
            <h1>Articles</h1>
            { articles.map((article: any) => (
                <Link
                    key={article.slug}
                    href={`/articles/${article.slug}`}
                >
                    <img src={article.cover} />
                    <h2>{article.title}</h2>
                    <p>{article.description}</p>
                </Link>
            ))}
        </main>
    );
}

error.tsx

'use client' // Error boundaries must be Client Components
 
export default function Error() {
  return (
    <> Error: Unable to load articles.</>
  )
}

loading.tsx

export default function Loading() {
  // Or a custom loading skeleton component
  return (<main>
            <h1>Articles</h1>
            <>Loading...</>
        </main>);
}

Upvotes: 0

Related Questions