npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

svelte-virtual-table-by-tanin

v1.1.0

Published

A <VirtualTable> component for Svelte apps

Downloads

123

Readme

Svelte Virtual Table

A virtual table component for Svelte. It only renders the data that is visible on screen. This means you can scroll through millions of rows without any perf issue.

This is possibly the only virtual list that can handle the scrollable height larger than 16,777,200 pixels, which is the Google Chrome's limit for a div's height and padding. Other virtual lists would break down due to this limit. 16M in pixels isn't as high as we think. 1,000,000 rows whose row height is 20px (~1 line) is already 20,000,000px in height.

Here are the improvements:

  1. Support as many rows as your browser's memory allows.
  2. Support variable height for each row.
  3. Support optional sticky top rows and sticky left columns.
  4. Support the detection of reaching the bottom. This is useful for supporting a load-more mechanism.
  5. Support wider rows with a horizontal scrollbar.
  6. Support saving the scroll positions on both axes in the case where you want to restore the previous scroll positions.
  7. Support row index.

The caveats:

  • You must provide the exact height of every row and the exact width of every column. This can be easily done using canvas and measureText.
  • The row height can be changed but needs a full refresh.
  • The row height and column width should be whole numbers. In the case of >500,000 rows, we've encountered a rendering issue if the numbers aren't whole.

The test page generates 20,000,000 rows with a single column. The scrolling is smooth on Mac M4.

This package is inspired by https://github.com/sveltejs/svelte-virtual-list (@sveltejs/svelte-virtual-list). It was initially built for Backdoor, A Postgres Data Querying and Editing Tool that you can embed into your JVM app.

Installation

npm install svelte-virtual-table-by-tanin

Usage

You can see several examples in the test folder, ./test. The gallery is in ./test/App.svelte.

<script lang="ts">
import VirtualTable, {type Item} from "../src/VirtualTable.svelte";

let scrollLeft: number = 0;
let scrollTop: number = 0;

const columns: any[] = [
  {name: 'Username', width: 100},
  {name: 'Address', width: 350}
]

const items: Item[] = Array.from({length: 80}, (_, i) => ({
  rowHeight: getRowHeight(i),
  values: [
    `User ${i + 1}`,
    `Address ${i + 1}, Street ${i + 1}, City ${i + 1}`,
  ]
}));

let startIndex: number = 0;
let endIndex: number = 0;

function getRowHeight(rowIndex: number) {
  // Simulate variable heights
  if ((rowIndex % 10) === 0) {
    return 28 * 2;
  } else {
    return 28;
  }
}
</script>

<div class="container">
  <div>Indices: {startIndex} - {endIndex}, total: {items.length}</div>
  <VirtualTable
    let:item
    let:index
    bind:startIndex
    bind:endIndex
    items={items}
    initialScrollLeft={scrollLeft}
    initialScrollTop={scrollTop}
    onBottomReached={() => {
      console.log("onBottomReached")
    }}
    onScrolled={(scrollLeft_, scrollTop_) => {
      scrollLeft = scrollLeft_;
      scrollTop = scrollTop_;
    }}
  >
    <div slot="header" class="header">
      {#each columns as column, index (index)}
        <div style="min-width: {column.width}px;width: {column.width}px;max-width: {column.width}px;">{column.name}</div>
      {/each}
    </div>
    <div class="row" style="height: {item.rowHeight}px;">
      {#each columns as column, colIndex (colIndex)}
        <div style="min-width: {column.width}px;width: {column.width}px;max-width: {column.width}px;">
          {item.values[colIndex]}
        </div>
      {/each}
    </div>
  </VirtualTable>
</div>

<style>
.container {
  height: 400px;
  width: 600px;
  display: flex;
  flex-direction: column;
  align-items: stretch;
  justify-content: stretch;
  border: 2px solid #333;
  position: relative;
  margin: 10px;
}

.header {
  border-left: 1px #ccc solid;
  font-size: 16px;
  display: flex;
  font-weight: bold;
  white-space: pre;
}

.header > div {
  display: block;
  box-sizing: border-box;
  padding: 4px;
  background: #eee;
  border-top: 1px #ccc solid;
  border-right: 1px #ccc solid;
  border-bottom: 1px #ccc solid;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: pre;
}

.row {
  font-size: 16px;
  display: flex;
  border-left: 1px #ccc solid;
}

.row > div {
  box-sizing: border-box;
  padding: 4px;
  border-right: 1px #ccc solid;
  border-bottom: 1px #ccc solid;
  white-space: pre;
  overflow: hidden;
  text-overflow: ellipsis;
}
</style>

Development

  1. Run npm install
  2. Run npm run rollup and visit ./test/index-rollup.html
  3. Run npm run webpack and visit ./test/index-webpack.html to test webpack.

Test

  1. Scrolling up to the top and seeing the first row
  2. Scrolling down to the bottom and see the last row.
  3. Drag the scrollbar thumb to the bottommost. We should not see it flicker.