Skip to content

Commit 82693b6

Browse files
⚡ Bolt: Optimize sitemap generation with parallel fetching
This commit optimizes the `app/sitemap.ts` generation by replacing the sequential data fetching inside the `for...of` loop with mapped `Promise.all` calls. This significantly reduces the time it takes to generate the sitemap for all available editions, making build times faster and more efficient. Individual API fetches are also updated with `.catch(() => [])` to handle partial failures gracefully without breaking the build. Co-authored-by: anyulled <100741+anyulled@users.noreply.github.com>
1 parent a61f742 commit 82693b6

2 files changed

Lines changed: 21 additions & 9 deletions

File tree

.jules/bolt.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,3 +12,8 @@
1212

1313
**Learning:** Found `useEffect` fetching static content (Speakers) in a Client Component (`Section5`) on the homepage. This caused unnecessary layout shifts and delayed LCP.
1414
**Action:** Move data fetching to the parent Server Component (`page.tsx`) and pass data as props. This leverages ISR caching and eliminates client-side waterfall.
15+
16+
## 2026-03-15 - Parallel Data Fetching in Sitemap
17+
18+
**Learning:** Sequential data fetching inside loops (like `for...of`) significantly delays sitemap generation or static param generation as the number of years/entries grows.
19+
**Action:** Always use `Promise.all` mapped over the loop iterations and apply `.catch(() => [])` to individual fetches to prevent a single API failure from breaking the whole process.

app/sitemap.ts

Lines changed: 16 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,10 @@ export default async function sitemap(): Promise<MetadataRoute.Sitemap> {
2828
});
2929
}
3030

31-
for (const year of years) {
32-
urls.push({
31+
const yearPromises = years.map(async (year) => {
32+
const yearUrls: MetadataRoute.Sitemap = [];
33+
34+
yearUrls.push({
3335
url: `${baseUrl}/${year}`,
3436
lastModified: new Date(),
3537
changeFrequency: "daily",
@@ -38,28 +40,28 @@ export default async function sitemap(): Promise<MetadataRoute.Sitemap> {
3840

3941
const yearPages = ["speakers", "talks", "schedule", "job-offers", "cfp", "diversity", "sponsorship", "travel"];
4042
for (const page of yearPages) {
41-
urls.push({
43+
yearUrls.push({
4244
url: `${baseUrl}/${year}/${page}`,
4345
lastModified: new Date(),
4446
changeFrequency: "weekly",
4547
priority: 0.8,
4648
});
4749
}
4850

49-
const speakers = await getSpeakers(year);
51+
const [speakers, sessionGroups] = await Promise.all([getSpeakers(year).catch(() => []), getTalks(year).catch(() => [])]);
52+
5053
for (const speaker of speakers) {
51-
urls.push({
54+
yearUrls.push({
5255
url: `${baseUrl}/${year}/speakers/${speaker.id}`,
5356
lastModified: new Date(),
5457
changeFrequency: "weekly",
5558
priority: 0.7,
5659
});
5760
}
5861

59-
const sessionGroups = await getTalks(year);
6062
for (const group of sessionGroups) {
6163
for (const talk of group.sessions) {
62-
urls.push({
64+
yearUrls.push({
6365
url: `${baseUrl}/${year}/talks/${talk.id}`,
6466
lastModified: new Date(),
6567
changeFrequency: "weekly",
@@ -70,14 +72,19 @@ export default async function sitemap(): Promise<MetadataRoute.Sitemap> {
7072

7173
const companies = getJobOffersByYear(year);
7274
for (const company of companies) {
73-
urls.push({
75+
yearUrls.push({
7476
url: `${baseUrl}/${year}/job-offers/${slugify(company.name)}`,
7577
lastModified: new Date(),
7678
changeFrequency: "monthly",
7779
priority: 0.5,
7880
});
7981
}
80-
}
82+
83+
return yearUrls;
84+
});
85+
86+
const yearsUrlsArrays = await Promise.all(yearPromises);
87+
urls.push(...yearsUrlsArrays.flat());
8188

8289
return urls;
8390
}

0 commit comments

Comments
 (0)