<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: JANG KIYOUNG</title>
    <description>The latest articles on DEV Community by JANG KIYOUNG (@thomasjang).</description>
    <link>https://dev.to/thomasjang</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F96694%2F79c17556-1615-4e8a-bc4f-d158002fafdf.jpg</url>
      <title>DEV Community: JANG KIYOUNG</title>
      <link>https://dev.to/thomasjang</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/thomasjang"/>
    <language>en</language>
    <item>
      <title>We Hit the Browser's Scroll Limit at 550K Rows. Here's How We Reached 1 Million.</title>
      <dc:creator>JANG KIYOUNG</dc:creator>
      <pubDate>Wed, 02 Sep 2026 00:47:53 +0000</pubDate>
      <link>https://dev.to/thomasjang/we-hit-the-browsers-scroll-limit-at-550k-rows-heres-how-we-reached-1-million-24go</link>
      <guid>https://dev.to/thomasjang/we-hit-the-browsers-scroll-limit-at-550k-rows-heres-how-we-reached-1-million-24go</guid>
      <description>&lt;h1&gt;
  
  
  We Hit the Browser's Scroll Limit at 550K Rows. Here's How We Reached 1 Million.
&lt;/h1&gt;

&lt;p&gt;A few days ago, we released &lt;strong&gt;BGrid (BeautifulGrid)&lt;/strong&gt;, an open-source&lt;br&gt;
React + TypeScript data grid under the Apache-2.0 license.&lt;/p&gt;

&lt;p&gt;One of the things we've been obsessing over is large datasets.&lt;/p&gt;

&lt;p&gt;Our first public demo handled:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;550,000 rows&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Honestly, 550K rows already felt like plenty.&lt;/p&gt;

&lt;p&gt;But as developers, we all know what happens when something works.&lt;/p&gt;

&lt;p&gt;We immediately ask:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;"Okay... but how far can we push it?"&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;So we tried going further.&lt;/p&gt;

&lt;p&gt;600K.&lt;/p&gt;

&lt;p&gt;700K.&lt;/p&gt;

&lt;p&gt;1 million.&lt;/p&gt;

&lt;p&gt;And then something weird happened.&lt;/p&gt;

&lt;p&gt;The grid didn't just get slower.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The layout started breaking.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fvi9n3ggelkalxdin3rre.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fvi9n3ggelkalxdin3rre.png" alt=" " width="800" height="488"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;React wasn't crashing.&lt;/p&gt;

&lt;p&gt;The browser wasn't out of memory.&lt;/p&gt;

&lt;p&gt;We weren't rendering 700,000 DOM nodes.&lt;/p&gt;

&lt;p&gt;So what was going on?&lt;/p&gt;

&lt;p&gt;Turns out, we had run into a completely different kind of limit.&lt;/p&gt;


&lt;h2&gt;
  
  
  The problem wasn't React
&lt;/h2&gt;

&lt;p&gt;BGrid uses virtual scrolling.&lt;/p&gt;

&lt;p&gt;That means if you have 550,000 rows, we obviously don't create 550,000&lt;br&gt;
&lt;code&gt;&amp;lt;div&amp;gt;&lt;/code&gt; elements.&lt;/p&gt;

&lt;p&gt;Only the rows currently visible in the viewport are actually rendered.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Dataset
─────────────────────
Row 1
Row 2
Row 3
...
Row 549,998
Row 549,999
Row 550,000
─────────────────────

        ↓

Actual DOM

Row 549,989
Row 549,990
Row 549,991
...
Row 550,000
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So the number of rendered DOM nodes stays small.&lt;/p&gt;

&lt;p&gt;Great.&lt;/p&gt;

&lt;p&gt;Problem solved, right?&lt;/p&gt;

&lt;p&gt;Well...&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Not quite.&lt;/strong&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Our invisible giant &lt;code&gt;&amp;lt;div&amp;gt;&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;Although we weren't rendering every row, the scrollbar still needed to&lt;br&gt;
represent the entire dataset.&lt;/p&gt;

&lt;p&gt;Our rows were approximately &lt;strong&gt;29px high&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;So for 550,000 rows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;550,000 × 29px = 15,950,000px
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Yes.&lt;/p&gt;

&lt;p&gt;Almost &lt;strong&gt;16 million pixels&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Basically, we had created an invisible skyscraper inside the browser.&lt;/p&gt;

&lt;p&gt;And the browser was politely telling us:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"No."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;😅&lt;/p&gt;

&lt;p&gt;At around this range in our target browsers, we started hitting&lt;br&gt;
practical CSS/layout limits for extremely large element dimensions.&lt;/p&gt;

&lt;p&gt;The physical scroll area could no longer grow reliably.&lt;/p&gt;

&lt;p&gt;That's when we realized something important:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Our virtualized rows weren't the problem.\&lt;br&gt;
Our virtualized scrollbar was.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;


&lt;h2&gt;
  
  
  Why 550K rows?
&lt;/h2&gt;

&lt;p&gt;This also explains why our original demo stopped at the strangely&lt;br&gt;
specific number of &lt;strong&gt;550,000 rows&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;It wasn't because BGrid suddenly became too slow at row 550,001.&lt;/p&gt;

&lt;p&gt;It was basically this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Row height: 29px

550,000 × 29px ≈ 15.95 million px
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We were approaching the practical physical height limit we were seeing&lt;br&gt;
in the browser.&lt;/p&gt;

&lt;p&gt;So we initially capped the demo around there.&lt;/p&gt;

&lt;p&gt;And for a while, that seemed reasonable.&lt;/p&gt;

&lt;p&gt;After all...&lt;/p&gt;

&lt;p&gt;Who needs more than 550,000 rows in a browser?&lt;/p&gt;

&lt;p&gt;Then the developer brain kicked in:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"But what if we didn't need a 16-million-pixel div at all?"&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;And that changed everything.&lt;/p&gt;


&lt;h1&gt;
  
  
  Stop scrolling physically
&lt;/h1&gt;

&lt;p&gt;The old model was simple.&lt;/p&gt;

&lt;p&gt;The number of rows directly determined the physical scroll height.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;OLD MODEL

550,000 rows
      ↓
× 29px
      ↓
15,950,000px
      ↓
Physical scroll area
      ↓
Browser says: that's enough
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This works beautifully...&lt;/p&gt;

&lt;p&gt;until it doesn't.&lt;/p&gt;

&lt;p&gt;So in &lt;strong&gt;BGrid v1.0.3&lt;/strong&gt;, we changed the architecture.&lt;/p&gt;

&lt;p&gt;Instead of treating the browser's physical scroll position as the actual&lt;br&gt;
dataset position, we separated them.&lt;/p&gt;

&lt;p&gt;We moved from:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Physical scrolling&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;to:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Logical scrolling&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;


&lt;h1&gt;
  
  
  Physical scroll ≠ Logical scroll
&lt;/h1&gt;

&lt;p&gt;The new idea is surprisingly simple.&lt;/p&gt;

&lt;p&gt;The browser only needs to scroll through a &lt;strong&gt;safe physical range&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;BGrid then maps that physical scroll position into the much larger&lt;br&gt;
&lt;strong&gt;logical dataset range&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Conceptually:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;NEW MODEL

1,000,000+ rows
        ↓
Logical scroll range
        ↓
Mapped to a bounded physical scroll range
        ↓
Calculate logical row position
        ↓
Render only visible rows
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So instead of saying:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;1 row = 29 physical pixels
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;for the entire dataset, we can think in terms of:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Physical scrollbar position
        ↓
Logical position
        ↓
Target row index
        ↓
Visible rows
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The browser no longer needs to know how physically tall one million rows&lt;br&gt;
would be.&lt;/p&gt;

&lt;p&gt;And that's a big difference.&lt;/p&gt;


&lt;h1&gt;
  
  
  Hello, 1,000,000 rows 👋
&lt;/h1&gt;

&lt;p&gt;  &lt;iframe src="https://www.youtube.com/embed/BBq4hPsNDNI" width="710" height="399"&gt;
  &lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;Once we removed the dependency on the full physical scroll height, the&lt;br&gt;
old 550K ceiling disappeared.&lt;/p&gt;

&lt;p&gt;So naturally, we tried:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;1,000,000 rows
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And it worked.&lt;/p&gt;

&lt;p&gt;That is now the large-data example in &lt;strong&gt;BGrid v1.0.3&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The important part isn't just that the grid can display one million&lt;br&gt;
rows.&lt;/p&gt;

&lt;p&gt;It's that we no longer need this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;1,000,000 × 29px = 29,000,000px
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;as a physical DOM height.&lt;/p&gt;

&lt;p&gt;Instead:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;1,000,000 rows

        ↓

logical scroll space

        ↓

bounded physical scrollbar

        ↓

virtualized viewport

        ↓

only visible rows rendered
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The dataset can grow without requiring the DOM element height to grow at&lt;br&gt;
the same rate.&lt;/p&gt;


&lt;h1&gt;
  
  
  So... what about 10 million?
&lt;/h1&gt;

&lt;p&gt;You know exactly what happened next.&lt;/p&gt;

&lt;p&gt;1 million worked.&lt;/p&gt;

&lt;p&gt;So obviously we typed another zero.&lt;/p&gt;

&lt;p&gt;😂&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;10,000,000 rows
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And yes.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;BGrid was able to display it.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Now, before someone opens Hacker News and starts sharpening their&lt;br&gt;
keyboard:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;No, we're not suggesting you should load 10 million database records&lt;br&gt;
into browser memory.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Please don't. 😄&lt;/p&gt;

&lt;p&gt;At that scale, server-side processing, pagination, incremental loading,&lt;br&gt;
or other data strategies will usually make much more sense.&lt;/p&gt;

&lt;p&gt;The 10M experiment wasn't about saying:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Everyone should put 10 million rows in React!"&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;It was about answering a different question:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Is the grid itself still fundamentally limited by the browser's&lt;br&gt;
maximum physical scroll height?&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;After switching to logical scrolling, the answer is:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;No.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;And that's what we really wanted to find out.&lt;/p&gt;




&lt;h1&gt;
  
  
  There are still limits, of course
&lt;/h1&gt;

&lt;p&gt;Logical scrolling doesn't magically give browsers infinite resources.&lt;/p&gt;

&lt;p&gt;At very large dataset sizes, you still have to think about:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  Memory usage&lt;/li&gt;
&lt;li&gt;  Data generation&lt;/li&gt;
&lt;li&gt;  Sorting&lt;/li&gt;
&lt;li&gt;  Filtering&lt;/li&gt;
&lt;li&gt;  Searching&lt;/li&gt;
&lt;li&gt;  Serialization&lt;/li&gt;
&lt;li&gt;  Network transfer&lt;/li&gt;
&lt;li&gt;  Application state management&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you load 10 million giant JavaScript objects into memory and your&lt;br&gt;
laptop starts sounding like a drone...&lt;/p&gt;

&lt;p&gt;That's probably not the grid's fault. 😅&lt;/p&gt;

&lt;p&gt;What logical scrolling solves is one specific architectural limitation:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;The maximum dataset size is no longer directly tied to the maximum&lt;br&gt;
physical height of a DOM element.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;And that gives us a much better foundation for large datasets.&lt;/p&gt;




&lt;h1&gt;
  
  
  BGrid isn't just a 1M-row demo
&lt;/h1&gt;

&lt;p&gt;Performance is fun to talk about because...&lt;/p&gt;

&lt;p&gt;well...&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1,000,000 ROWS&lt;/strong&gt; looks cool in a headline. 😎&lt;/p&gt;

&lt;p&gt;But BGrid isn't intended to be a benchmark toy.&lt;/p&gt;

&lt;p&gt;We're building it for actual business applications.&lt;/p&gt;

&lt;p&gt;It currently includes features such as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  Virtual scrolling&lt;/li&gt;
&lt;li&gt;  Cell editing&lt;/li&gt;
&lt;li&gt;  Cell merging&lt;/li&gt;
&lt;li&gt;  Sorting&lt;/li&gt;
&lt;li&gt;  Filtering&lt;/li&gt;
&lt;li&gt;  Summary&lt;/li&gt;
&lt;li&gt;  Pivot&lt;/li&gt;
&lt;li&gt;  Frozen rows&lt;/li&gt;
&lt;li&gt;  Frozen columns&lt;/li&gt;
&lt;li&gt;  Custom editors&lt;/li&gt;
&lt;li&gt;  Keyboard navigation&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It's built with &lt;strong&gt;React + TypeScript&lt;/strong&gt; and released under the&lt;br&gt;
&lt;strong&gt;Apache-2.0 license&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;No commercial license is required for the open-source features.&lt;/p&gt;




&lt;h1&gt;
  
  
  Why build another React data grid?
&lt;/h1&gt;

&lt;p&gt;Fair question.&lt;/p&gt;

&lt;p&gt;There are already some excellent data grids in the React ecosystem.&lt;/p&gt;

&lt;p&gt;But we've been building business applications --- and grids --- for a long time.&lt;/p&gt;

&lt;p&gt;We wanted something that was:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  Open source&lt;/li&gt;
&lt;li&gt;  Business-application focused&lt;/li&gt;
&lt;li&gt;  Strongly typed&lt;/li&gt;
&lt;li&gt;  React-friendly&lt;/li&gt;
&lt;li&gt;  Easy for developers to customize&lt;/li&gt;
&lt;li&gt;  Easy for AI coding agents to understand and use&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That last point has become increasingly interesting to us.&lt;/p&gt;

&lt;p&gt;In a world where developers are writing more software together with AI,&lt;br&gt;
good types, predictable APIs, examples, and documentation aren't just&lt;br&gt;
developer experience anymore.&lt;/p&gt;

&lt;p&gt;They're also &lt;strong&gt;AI developer experience&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;That's something we're actively experimenting with in BGrid.&lt;/p&gt;




&lt;h1&gt;
  
  
  Try to break it
&lt;/h1&gt;

&lt;p&gt;The public demo currently uses &lt;strong&gt;1,000,000 rows&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;And honestly, we'd love for people to try to break it.&lt;/p&gt;

&lt;p&gt;👉 &lt;strong&gt;Demo:&lt;/strong&gt; &lt;a href="https://bgrid.axisj.com/" rel="noopener noreferrer"&gt;https://bgrid.axisj.com/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;👉 &lt;strong&gt;GitHub:&lt;/strong&gt; &lt;a href="https://github.com/axisj/beautiful-grid" rel="noopener noreferrer"&gt;https://github.com/axisj/beautiful-grid&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you're building React applications, give it a try.&lt;/p&gt;

&lt;p&gt;If something breaks, please open an issue.&lt;/p&gt;

&lt;p&gt;If you think the architecture is wrong, tell us.&lt;/p&gt;

&lt;p&gt;If you know a better way to solve the scrolling problem, we'd genuinely&lt;br&gt;
love to hear it.&lt;/p&gt;

&lt;p&gt;And if you like the project, a GitHub ⭐ would help a young open-source&lt;br&gt;
project a lot.&lt;/p&gt;




&lt;p&gt;One question for other frontend developers:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;What's the largest dataset you've ever had to display in a&lt;br&gt;
browser?&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;And more importantly...&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;what broke first?&lt;/strong&gt; 😄&lt;/p&gt;

</description>
      <category>react</category>
      <category>typescript</category>
      <category>webdev</category>
      <category>showdev</category>
    </item>
  </channel>
</rss>
