Skip to content

Why the "Popular" Marquee Across Three Pillars Looks Empty — and How to Fill It

Adityo Guni Waluyo

Marquee populer 3 pilar (olahraga/kebudayaan/kepemudaan) kosong di pilar muda. Fix: parallel fetch popular + random, mergePopularWithRandom dedupe by id, popular priority, slice 9.

TL;DR

KotaPortal's marquee looked half-empty. Server-side parallel fetch and merge dedupe fixed this. Young pillars now display 9 items.

The Problem: Young Pillars, Half-Empty Marquees

I opened the olahraga, kebudayaan, and kepemudaan pages on KotaPortal and each had a vertical "Popular" marquee. But the newer pillars — like kepemudaan — only had 3-4 popular items. The marquee looked half-empty, which looks broken.

The Fix: Parallel Fetch + Merge Dedupe Server-Side

Commit a5f93eb fixes this with a server-side parallel fetch + merge dedupe pattern. Each pillar fetches two things in parallel: sort=popular (limit 9) + sort=random (limit 9, presentation=visual). Then mergePopularWithRandom combines them: popular first, dedupe by id, slice to 9. The marquee stays full at 9 items even for young pillars.

Why Parallel Fetch?

Next.js says parallel data fetching reduces waterfalls and total load time [4]. Sequential would make random wait for popular — wasted time.

Merge Logic

The merge lives in a server component (populer.ts), not the client. Map-based merge: existing entities (popular) never overwritten, novel entities (random) appended [5]. Dedupe by id prevents duplicates. Popular priority — if an item exists in both lists, the popular version wins.

function mergePopularWithRandom(popular: Item[], random: Item[]): Item[] {
  const seen = new Set()
  const merged: Item[] = []
  for (const item of popular) {
    if (!seen.has(item.id)) {
      seen.add(item.id)
      merged.push(item)
    }
  }
  for (const item of random) {
    if (!seen.has(item.id) && merged.length < 9) {
      seen.add(item.id)
      merged.push(item)
    }
  }
  return merged
}

Next.js RSC executes sequentially within a tree [6]. But here we fetch inside one server component (page.tsx), so Promise.all([fetchPopular(), fetchRandom()]) runs in parallel. No component restructure needed.

Test Coverage

populer.test.ts covers merge logic (popular priority, dedupe, slice 9, empty cases). All green.

Sources

  1. Next.js: Parallel data fetching reduces waterfalls
  2. FrontendCache: Map-based merge utility for idempotent dedupe
  3. Vercel: RSC execute sequentially; restructure with composition for parallel fetch

Related articles