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

@cujuju/solidjs-virtual-log

v0.2.0

Published

Bottom-anchored virtual log for SolidJS (chat, logs, feeds): measured rows, no scroll writes while the user scrolls or flings (iOS-safe), older-row paging with a runway, keyboard row navigation.

Readme

@cujuju/solidjs-virtual-log

A virtualized, bottom-anchored log for SolidJS: chat, logs, activity feeds. Rows are measured as they draw, older rows page in as the top nears, and the scroll offset is never written while the user is scrolling — the property that keeps iOS momentum scrolling smooth.

import { createVirtualLog, VirtualLog } from '@cujuju/solidjs-virtual-log';

const log = createVirtualLog({ rows, estimateSize: 44, hasOlder, loadOlder, following });

<div ref={log.ref} style={{ height: '100%', 'overflow-y': 'auto' }}>
  <VirtualLog log={log} onRowMenu={openMenu}>{(row) => <Message msg={row()} />}</VirtualLog>
</div>;

Model

  • Bottom-anchored. ref makes the scroller column-reverse, so offsets count up from the newest row. Rows loaded or measured above the view never move it, with no scroll write.
  • Measured by key. One ResizeObserver reads each row's border box; sizes stay with row keys across prepends, appends and replacements.
  • One anchor rule. After rows change size or come and go, the view keeps the row cut by its top edge in place (reading history), keeps its bottom edge (following, scrolled slightly up), stays on the newest row (following, at the bottom), or keeps a held row centered.
  • Corrections wait for rest. While a touch, mouse button, wheel, keyboard or momentum scroll is active, a correction only translates the rows (shift); the native scroll offset and extent are untouched. Nearing the newest row, the shift is folded away in step with the scroll, so there is no false bottom. Once the scroller rests (no input, no movement, in bounds for 3 frames and 150ms), the extent and offset are set in one step, invisibly.
  • Runway. While hasOlder(), blank space above the oldest row lets a fling run on while the next page loads.

API

createVirtualLog(options):

| option | | |---|---| | rows | Accessor<readonly R[]>, oldest first | | getKey | default row.key | | estimateSize | px, or per row | | overscanPx, overscan | drawn beyond the view: px (default one viewport) plus rows (default 4) | | endPadding | space under the newest row | | following | the view follows the newest row | | hasOlder, loadOlder, olderThreshold | paging; loads when the top edge is within olderThreshold rows (10) or two viewports | | runwayPx | default 3 viewports | | layoutKey | changing it (density, font) marks measurements stale |

Returns ref, keys, allKeys, rowByKey, startOf, extent, shift, distanceFromBottom, distanceFromTop, inViewKey, bottomOf(key), scrollToKey(key, { align }), holdRow(key | null, align?), holding, scrollToBottom, isScrolling, checkOlder. align is 'auto', 'center' (holdRow's default) or { bottom }: the row's bottom edge that many pixels above the view's bottom, as bottomOf reads it, so a saved place can be restored.

<VirtualLog log class? onRowMenu?>: one Tab stop (the last focused row, else the newest in view); ArrowUp/ArrowDown move between rows; the ContextMenu key or Shift+F10 calls onRowMenu(rowEl).

Limits

  • A correction held mid-scroll (posts arriving, rows below growing) is folded away as the view nears the newest row: content runs up to 2x the scroll over that stretch, so the newest row arrives exactly at the bottom.
  • A fling longer than the runway still stops at the loaded top.
  • Media without a reserved size grows after it loads, and the rows around it move by that growth (above it while following the newest row, below it otherwise). Reserve media boxes where the size is known.
  • Playwright's WebKit is not iOS: check momentum and rubber-banding on a device.