Reputation: 16669
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
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