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

@lcabrera/ui

v0.2.0

Published

React 19 component library built around a virtualized, filterable data table. StyleX-styled, React Router framework mode.

Readme

@lcabrera/ui

A React 19 component library built around one hard problem: a data table that stays responsive with tens of thousands of rows — virtualized, filterable, sortable, column-pinnable, resizable, with server-driven filter options and infinite scroll. Everything else in the package grew out of making that work.

Styled exclusively with StyleX, built for React Router in framework mode, and designed so that re-renders are governed by granular store subscriptions rather than by memoization.

Install

npm install @lcabrera/ui

Peer dependencies

Framework singletons are peers on purpose — as ordinary dependencies your app would resolve a second copy of React, which breaks hooks outright.

npm install react react-dom react-router @stylexjs/stylex

| Peer | Range | Notes | | -------------------- | --------- | ------------------------------------------------------ | | react | ^19.0.0 | Uses use(), Actions, and the React Compiler | | react-dom | ^19.0.0 | | | react-router | ^8.0.0 | Framework mode — loaders, actions, useLoaderData | | @stylexjs/stylex | ^0.19.0 | | | @react-router/node | ^8.0.0 | Optional — only the ./entry/* SSR helpers use it |

@react-router/node is an optional peer so a browser-only consumer is not forced to install it.

Before you install: this package ships TypeScript source

@lcabrera/ui is published as source, not as a compiled bundle — unlike its three sibling packages, which ship .mjs + .d.mts. StyleX resolves its style definitions at build time, so the styles have to be compiled in your build, against your theme, to be themeable and to dedupe with your own styles.

That means your bundler must:

  1. Compile TypeScript/JSX from node_modules/@lcabrera/ui — most setups exclude node_modules from transpilation by default.
  2. Run the StyleX plugin over the package source, with an alias so StyleX can resolve the package's internal imports.

With Vite:

import { unplugin as stylex } from '@stylexjs/unplugin';
import { fileURLToPath } from 'node:url';
import babel from 'vite-plugin-babel';

const uiSrc = fileURLToPath(
  new URL('node_modules/@lcabrera/ui/src/', import.meta.url),
);

export default {
  plugins: [
    stylex.vite({
      aliases: { '@lcabrera/ui/*': [`${uiSrc}*`] },
      useCSSLayers: true,
    }),
    babel({
      babelConfig: {
        parserOpts: { plugins: ['jsx'] },
        plugins: [['babel-plugin-react-compiler']],
        presets: [['@babel/preset-typescript', { ignoreExtensions: true }]],
      },
      include: /@lcabrera\/ui\/src\/(?!.*\.test\.).*\.[jt]sx?(\?.*)?$/,
    }),
  ],
};

If that is more build surface than you want, take the package as a reference implementation rather than a dependency — the source is the documentation, and every component carries an ARCHITECTURE.md.

What's in it

Import from the root barrel for the handful of top-level pieces, or from a subpath for anything else. There is deliberately no single mega-barrel: a subpath import is what keeps an unused component out of your bundle.

import { Button, Form, TableLayout } from '@lcabrera/ui';
import { Modal } from '@lcabrera/ui/components/Modal';
import { useVirtualization } from '@lcabrera/ui/hooks';

A component subpath resolves through that directory's index.ts, so it needs a bundler's directory resolution — which this package requires in any case, per the section above.

Root barrel — @lcabrera/ui

AppDocument, AppProviders, AppShell, Button, Form, JsonExplorer, NavLink, RootErrorBoundary, RouteErrorBoundary, SectionCard, StatusBadge, TableLayout, Tabs, hydrateApp, useNotifyOnError, plus the FieldNode, LayoutProps and Pagination types.

Components — @lcabrera/ui/components/*

| Area | Components | | -------------- | -------------------------------------------------------------------------------------------------------------------- | | Data | Table, StaticTable, VirtualList, VirtualSelect, TrendSparkline, JsonExplorer | | Forms | Form, Checkbox, RadioOptionGroup, ToggleSwitch, DraggableList | | Overlays | Modal, ChoiceModal, ConfirmDialog, SidePanel, PinSideModal, Tooltip, NotificationCenter | | Layout | AppShell, AppDocument, AppProviders, AppBackground, AppNavigation, Navbar, Card, Tabs | | Primitives | Button, ActionButtons, CopyButton, Icons, InfoBox, NavLink, SectionCard, StatusBadge, Tag, Title | | Feedback | RootErrorBoundary, RouteErrorBoundary, MarkdownRenderer, Settings |

Hooks — @lcabrera/ui/hooks

useBackNavigate, useClickOutside, useElementSize, useInfiniteScrollObserver, useNotifyOnError, usePersistCookieAction, useResizeObserver, useStore, useVirtualization.

Everything else

| Subpath | What it holds | | ------------------------------ | ------------------------------------------------------------------------- | | @lcabrera/ui/design-system/* | StyleX tokens, light/dark themes, a CSS reset | | @lcabrera/ui/contexts/* | Global settings, notifications, theme — each a store-pattern provider | | @lcabrera/ui/routing/* | Loader/action helpers: URL state, cookie persistence, filter sanitisation | | @lcabrera/ui/utils/* | Filters, prefetch, storage, theme, URL state, a logger | | @lcabrera/ui/types/* | Shared type definitions | | @lcabrera/ui/entry/* | SSR entry helpers (needs @react-router/node) | | @lcabrera/ui/server | createHandleRequest — the SSR request handler |

Usage

A full data table

TableLayout is the assembled table: header, virtualized body, settings and column drawers, filters, sorting, pinning, infinite scroll. You give it a promise and tell it how to read rows out of the response.

import type { Pagination } from '@lcabrera/ui';

import { TableLayout } from '@lcabrera/ui';
import { useLoaderData } from 'react-router';

export const Orders = () => {
  const { columnsState, ordersPromise, metaState } =
    useLoaderData<typeof loader>();

  const handleLoadMore = async ({ limit, skip }: Pagination) =>
    fetchOrdersPage({ limit, skip });

  return (
    <TableLayout<Order, OrdersResponse>
      columnsState={columnsState}
      dataPromise={ordersPromise}
      dataSelector={(response) => response.data}
      dataTotalSelector={(response) => response.total}
      metaState={metaState}
      onLoadMore={handleLoadMore}
    />
  );
};

dataPromise is passed unresolved — the table suspends on it, so the shell, header and skeleton stream to the browser before the rows exist. onLoadMore is what infinite scroll calls as you approach the end of the loaded range.

The app shell

AppShell renders the navigation chrome and the routed outlet; it takes the navigation items from your app rather than owning them. AppProviders supplies theme and global-settings state, seeded from the root loader so SSR and the browser agree on the first paint.

import { AppProviders, AppShell } from '@lcabrera/ui';
import { useLoaderData } from 'react-router';

export const Root = () => {
  const { globalSettings, theme } = useLoaderData<typeof rootLoader>();

  return (
    <AppProviders
      appId='my-app'
      defaultTheme='light'
      globalSettings={globalSettings}
      initialTheme={theme}
    >
      <AppShell getNavigationItems={getNavigationItems} />
    </AppProviders>
  );
};

appId scopes the theme and settings cookies, so two apps on the same host do not overwrite each other's preferences.

Virtualization on its own

The table's virtualization is available as a hook if that is the only part you want. It measures the container itself and re-measures on resize, so you supply the item height and total count and render the window it hands back.

import { useVirtualization } from '@lcabrera/ui/hooks';

const { bottomSpacerHeight, endIndex, offsetY, startIndex, totalHeight } =
  useVirtualization({
    containerRef,
    itemHeight: 36,
    overscan: 8,
    totalItems: rows.length,
  });

How it is built

Four decisions explain most of the code, and they are worth knowing before you extend anything:

  • Split contexts + external stores, not prop drilling. Table state lives in useSyncExternalStore-backed stores split by concern, and components subscribe through selectors. A cell that depends on one column's width re-renders when that width changes — not when any table state changes.
  • The React Compiler owns memoization. There is almost no useMemo or memo() here by design. Performance comes from the subscription granularity above, from row virtualization, and from streaming — not from hand-tuning.
  • StyleX only. No CSS modules, no styled-components, no inline styles, no Tailwind. Styles are colocated in *.stylex.ts files and composed in a fixed order so overrides are predictable.
  • Client-safe by construction. A publish gate fails the build if anything in this package's dependency closure reaches for node:*. That is why the HTTP helpers live in @lcabrera/api and the Postgres code in @lcabrera/server, rather than all three sharing one package.

Every component directory carries an ARCHITECTURE.md covering its props, render flow and constraints; src/PATTERNS.md covers the conventions across all of them, and src/INVENTORY.md catalogues every artifact.

Links

MIT © Lucio Cabrera