DEV Community

Yunhan
Yunhan

Posted on • Originally published at babynamepick.com

Lazy Loading 2,800+ Baby Names: How We Cut Page Size by 77%

When your baby name website grows to 3,000+ names, page performance becomes a real challenge. Here's how we solved it.

The Problem

Our browse page loads names grouped by origin — Irish, French, Japanese, Hebrew, and 40+ more categories. With 2,800+ names, the page was shipping 3.6MB of HTML on first load. Category pages like Popular Names had similar issues.

Users on mobile were waiting 4-6 seconds for the page to become interactive. Not great for a tool people use while excitedly planning their baby's name.

The Solution: Progressive Disclosure

Instead of loading all names at once, we show the first 30 names per section and let users expand with a "Show all X names" button.

Architecture

We kept our pages as server components for SEO (search engines see all names), but wrapped the display logic in client components:

// BrowseNameList.tsx - Client Component
"use client";
import { useState } from "react";

export default function BrowseNameList({ groupedNames }) {
  return (
    <>
      {Object.entries(groupedNames).map(([origin, names]) => (
        <OriginSection key={origin} origin={origin} names={names} />
      ))}
    </>
  );
}

function OriginSection({ origin, names }) {
  const [expanded, setExpanded] = useState(false);
  const visible = expanded ? names : names.slice(0, 30);

  return (
    <section>
      <h2>{origin} Names ({names.length})</h2>
      <div className="grid grid-cols-2 md:grid-cols-3 lg:grid-cols-4 gap-4">
        {visible.map(name => <NameCard key={name.slug} {...name} />)}
      </div>
      {names.length > 30 && !expanded && (
        <button onClick={() => setExpanded(true)}>
          Show all {names.length} names
        </button>
      )}
    </section>
  );
}
Enter fullscreen mode Exit fullscreen mode

Results

Page Before After Reduction
/browse 3.6MB 820KB 77%
/popular-names 1.3MB 220KB 83%
/modern-names 3.1MB ~400KB 87%

Key Decisions

1. Server components stay server components. All SEO metadata, JSON-LD schema, and the full name list are still rendered server-side. Search engines see everything.

2. Client components only handle display logic. The useState for expand/collapse is the only client-side state. No data fetching, no side effects.

3. Each origin section manages its own state. Users expanding "Irish Names" doesn't affect "French Names." Independent, predictable behavior.

Why Not Pagination?

We considered traditional pagination but rejected it:

  • SEO penalty: Multiple paginated pages split link equity
  • UX friction: Users hate clicking "Page 2, 3, 4..."
  • Search intent mismatch: People browsing baby names want to scan quickly, not navigate pages

Progressive disclosure gives us the best of both worlds — fast initial load AND all content accessible without navigation.

What's Next

We're exploring:

  • Virtual scrolling for the expanded view (react-window)
  • Intersection Observer to lazy-load sections as users scroll
  • Image lazy loading for name cards with famous people photos

Check out the live implementation at babynamepick.com/browse, or explore specific categories like Irish Names, Rare Names, or French Baby Names.

We've also been writing about the journey of building this site:


Building BabyNamePick — a free baby name search engine with 3,000+ names, meanings, and origins. Open to feedback!

Top comments (0)