1

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>
            ))}
        </>
    );
}
1
  • how does your ErrorBoundary component look like? I found one example w/ a class component and this doesn't work with server components :( Commented Dec 3, 2024 at 14:04

1 Answer 1

0

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>);
}
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.