🚀 Scaling Client-Side Search: 100,000 Users, Skills, and Real-Time Filtering in React

Today, I came across a post on dev.to from Fatemeh Paghar on using useTransition and react-window. I decided to push a typical React search/filter UI to the extreme by scaling it up to handle 100,000 users — not a typo — one hundred thousand records cl…


This content originally appeared on DEV Community and was authored by Aldorax

Today, I came across a post on dev.to from Fatemeh Paghar on using useTransition and react-window. I decided to push a typical React search/filter UI to the extreme by scaling it up to handle 100,000 users — not a typo — one hundred thousand records client-side.

Instead of simple emails, each user now has:

  • A name
  • A skillset (e.g., React, Python, AWS)

To make it even more interesting, I added a second search field — but with a twist:

You must first search by name before the skill search field becomes active.

🛠 How It Works

  • First input: Search by name (e.g., “Alice”, “Bob”)
  • Second input: Search by skill (e.g., “React”, “Rust”), but it remains disabled until a name is entered.

Once a name is typed, the skill search input unlocks and users can further refine their search.

⚡ First: The Basic Naive Version (No Optimization)

Let's look at a simple implementation first — without react-window and without useMemo.

"use client"
import React, { useState, useTransition } from "react";

const generateUsers = (count: number) => {
  const names = ["Alice", "Bob", "Charlie", "David", "Eve", "Frank", "Grace", "Helen"];
  const skills = ["React", "Python", "Node.js", "Django", "Rust", "Go", "Flutter", "AWS"];

  return Array.from({ length: count }, (_, i) => ({
    id: i,
    name: `${names[i % names.length]} ${i}`,
    skill: skills[i % skills.length],
  }));
};

const usersData = generateUsers(100000);

export default function BasicMassiveSearch() {
  const [nameQuery, setNameQuery] = useState("");
  const [skillQuery, setSkillQuery] = useState("");
  const [isPending, startTransition] = useTransition();

  const handleNameSearch = (e: React.ChangeEvent<HTMLInputElement>) => {
    const value = e.target.value;
    startTransition(() => setNameQuery(value));
  };

  const handleSkillSearch = (e: React.ChangeEvent<HTMLInputElement>) => {
    const value = e.target.value;
    startTransition(() => setSkillQuery(value));
  };

  const filteredUsers = usersData.filter((user) => {
    const matchesName = user.name.toLowerCase().includes(nameQuery.toLowerCase());
    if (!matchesName) return false;
    if (skillQuery) {
      return user.skill.toLowerCase().includes(skillQuery.toLowerCase());
    }
    return true;
  });

  return (
    <div className="p-6">
      <h1 className="text-2xl font-bold mb-4">Simple Skill Search</h1>

      <input
        type="text"
        value={nameQuery}
        onChange={handleNameSearch}
        placeholder="Search by name..."
        className="border p-2 mb-4 block w-full"
      />

      <input
        type="text"
        value={skillQuery}
        onChange={handleSkillSearch}
        disabled={!nameQuery}
        placeholder="Search by skill..."
        className="border p-2 mb-4 block w-full"
      />

      {isPending && <p className="italic text-sm">Filtering...</p>}

      <div className="mt-4">
        {filteredUsers.length === 0 ? (
          <p className="italic text-gray-500">No users found.</p>
        ) : (
          filteredUsers.map((user) => (
            <div key={user.id} className="p-2 border-b">
              <p className="font-bold">{user.name}</p>
              <p className="text-sm text-gray-600">{user.skill}</p>
            </div>
          ))
        )}
      </div>
    </div>
  );
}

😵 Problems With This Naive Version

  • No Virtualization: Every single result (up to 100,000 DOM elements) renders at once. ➔ Browser lags, page freezes, and scrolling becomes painful.
  • No Memoization: Every re-render recomputes .filter() unnecessarily. ➔ Causes typing delays as the dataset grows.
  • Performance collapse: Mobile users and low-end laptops will suffer first.

✅ Now: The Optimized Version (Virtualized + Memoized)

"use client"
import React, { useState, useMemo, useTransition } from "react";
import { FixedSizeList as List } from "react-window";

const generateUsers = (count: number) => {
  const names = ["Alice", "Bob", "Charlie", "David", "Eve", "Frank", "Grace", "Helen"];
  const skills = ["React", "Python", "Node.js", "Django", "Rust", "Go", "Flutter", "AWS"];

  return Array.from({ length: count }, (_, i) => ({
    id: i,
    name: `${names[i % names.length]} ${i}`,
    skill: skills[i % skills.length],
  }));
};

const usersData = generateUsers(100000);

export default function MassiveSkillSearch() {
  const [nameQuery, setNameQuery] = useState("");
  const [skillQuery, setSkillQuery] = useState("");
  const [isPending, startTransition] = useTransition();
  const [searchDuration, setSearchDuration] = useState<number>(0);

  const filteredUsers = useMemo(() => {
    const start = performance.now();

    const lowerName = nameQuery.toLowerCase();
    const lowerSkill = skillQuery.toLowerCase();

    const result = usersData.filter((user) => {
      const matchesName = user.name.toLowerCase().includes(lowerName);
      if (!matchesName) return false;
      if (lowerSkill) {
        return user.skill.toLowerCase().includes(lowerSkill);
      }
      return true;
    });

    const end = performance.now();
    setSearchDuration(end - start);
    return result;
  }, [nameQuery, skillQuery]);

  const handleNameSearch = (e: React.ChangeEvent<HTMLInputElement>) => {
    const value = e.target.value;
    startTransition(() => setNameQuery(value));
  };

  const handleSkillSearch = (e: React.ChangeEvent<HTMLInputElement>) => {
    const value = e.target.value;
    startTransition(() => setSkillQuery(value));
  };

  const Row = ({ index, style }: { index: number; style: React.CSSProperties }) => {
    const user = filteredUsers[index];
    return (
      <div style={style} key={user.id} className="p-3 border-b border-green-100 hover:bg-green-50">
        <p className="font-medium text-gray-800">{user.name}</p>
        <p className="text-sm text-gray-500">{user.skill}</p>
      </div>
    );
  };

  return (
    <div className="p-6">
      <h1 className="text-2xl font-bold mb-4">🧠 Skillset Search (Virtualized)</h1>

      <input
        type="text"
        value={nameQuery}
        onChange={handleNameSearch}
        placeholder="Search by name..."
        className="border p-2 mb-4 block w-full"
      />

      <input
        type="text"
        value={skillQuery}
        onChange={handleSkillSearch}
        disabled={!nameQuery}
        placeholder="Search by skill..."
        className="border p-2 mb-4 block w-full"
      />

      {isPending && <p className="italic text-sm">Filtering...</p>}

      {filteredUsers.length === 0 ? (
        <p className="italic text-gray-500">No users found.</p>
      ) : (
        <List height={500} itemCount={filteredUsers.length} itemSize={70} width="100%">
          {Row}
        </List>
      )}

      <div className="mt-6 text-sm text-gray-600 text-center">
        <p>Total users: {usersData.length}</p>
        <p>Filtered users: {filteredUsers.length}</p>
        <p>Last search took: {searchDuration.toFixed(2)} ms</p>
        <p>{isPending ? "Searching..." : "Idle"}</p>
      </div>
    </div>
  );
}

🧠 What Changed — Why It Matters

Feature Basic Version Optimized Version
Rendering 100,000 real DOM nodes Only ~20 visible DOM nodes
Filtering Recalculates .filter() every render Memorized with useMemo, recalculates only when query changes
Scrolling Laggy and crash-prone Smooth and buttery
Typing Freezes momentarily on big datasets Remains fluid
CPU Usage Very high Low and efficient

🏁 Final Verdict

✅ If you are just prototyping or handling small datasets (few hundred items),

you can survive without virtualization and memoization.

❗ But the moment you deal with tens of thousands of records (or more),

you must:

  • Virtualize large lists
  • Use useMemo smartly
  • Possibly move search server-side

Otherwise, your app will become unusable — fast.

🎯 Closing Note

React gives you amazing flexibility, but scale tests your architecture.

Always think about memory, CPU, and user experience as you grow datasets!


This content originally appeared on DEV Community and was authored by Aldorax


Print Share Comment Cite Upload Translate Updates
APA

Aldorax | Sciencx (2025-04-26T01:30:18+00:00) 🚀 Scaling Client-Side Search: 100,000 Users, Skills, and Real-Time Filtering in React. Retrieved from https://www.scien.cx/2025/04/26/%f0%9f%9a%80-scaling-client-side-search-100000-users-skills-and-real-time-filtering-in-react/

MLA
" » 🚀 Scaling Client-Side Search: 100,000 Users, Skills, and Real-Time Filtering in React." Aldorax | Sciencx - Saturday April 26, 2025, https://www.scien.cx/2025/04/26/%f0%9f%9a%80-scaling-client-side-search-100000-users-skills-and-real-time-filtering-in-react/
HARVARD
Aldorax | Sciencx Saturday April 26, 2025 » 🚀 Scaling Client-Side Search: 100,000 Users, Skills, and Real-Time Filtering in React., viewed ,<https://www.scien.cx/2025/04/26/%f0%9f%9a%80-scaling-client-side-search-100000-users-skills-and-real-time-filtering-in-react/>
VANCOUVER
Aldorax | Sciencx - » 🚀 Scaling Client-Side Search: 100,000 Users, Skills, and Real-Time Filtering in React. [Internet]. [Accessed ]. Available from: https://www.scien.cx/2025/04/26/%f0%9f%9a%80-scaling-client-side-search-100000-users-skills-and-real-time-filtering-in-react/
CHICAGO
" » 🚀 Scaling Client-Side Search: 100,000 Users, Skills, and Real-Time Filtering in React." Aldorax | Sciencx - Accessed . https://www.scien.cx/2025/04/26/%f0%9f%9a%80-scaling-client-side-search-100000-users-skills-and-real-time-filtering-in-react/
IEEE
" » 🚀 Scaling Client-Side Search: 100,000 Users, Skills, and Real-Time Filtering in React." Aldorax | Sciencx [Online]. Available: https://www.scien.cx/2025/04/26/%f0%9f%9a%80-scaling-client-side-search-100000-users-skills-and-real-time-filtering-in-react/. [Accessed: ]
rf:citation
» 🚀 Scaling Client-Side Search: 100,000 Users, Skills, and Real-Time Filtering in React | Aldorax | Sciencx | https://www.scien.cx/2025/04/26/%f0%9f%9a%80-scaling-client-side-search-100000-users-skills-and-real-time-filtering-in-react/ |

Please log in to upload a file.




There are no updates yet.
Click the Upload button above to add an update.

You must be logged in to translate posts. Please log in or register.