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

@vdnp/tablekit-react-native

v0.1.0-beta.0

Published

React Native adapter for TableKit: useDataTable() headless hook + virtualized <DataTable /> built on FlatList (FlashList injectable).

Readme

@vdnp/tablekit-react-native

The React Native adapter for @vdnp/tablekit-core — the same headless engine and hook contract as @vdnp/tablekit-react, rendered with native primitives and virtualized through FlatList (or FlashList, injected).

Quick start

npm i @vdnp/tablekit-react-native @vdnp/tablekit-theme
# recommended for large lists:
npm i @shopify/flash-list

Requires React Native ≥ 0.72 and React ≥ 18. react and react-native are peer dependencies.

import { DataTable, createColumnHelper } from "@vdnp/tablekit-react-native";

interface Player {
  id: number;
  name: string;
  team: string;
  points: number;
}

const helper = createColumnHelper<Player>();
const columns = [
  helper.accessor("name", { header: "Name", sortable: true, width: 140 }),
  helper.accessor("team", { header: "Team", sortable: true }),
  helper.accessor("points", { header: "Pts", sortable: true, align: "right", width: 70 }),
];

export function Roster({ players }: { players: Player[] }) {
  return (
    <DataTable<Player>
      columns={columns}
      data={players}
      getRowId={(player) => String(player.id)}
      showGlobalFilter
      onRowPress={(row) => console.log(row.original.name)}
    />
  );
}

Core concepts

Same core, native gestures

The column API, state model and useDataTable() hook are identical to the web adapter — only the interaction idioms change to match the platform:

| Web (@vdnp/tablekit-react) | React Native (@vdnp/tablekit-react-native) | | --- | --- | | onRowClick | onRowPress | | — | onRowLongPress (native context-action idiom) | | CSS styles.css + CSS variables | theme / colorScheme token objects | | resizable drag handle | not applicable (fixed / flex widths) |

Everything else — enableSelection, bulkActions / onBulkAction, showGlobalFilter, showPagination, renderLoading / renderEmpty / renderError, renderExpandedRow, controlled/uncontrolled state, server-mode fetchData — works exactly as on the web.

Headless usage

Identical to the web hook — render with native components:

import { useDataTable } from "@vdnp/tablekit-react-native";
import { FlatList, Text, View } from "react-native";

interface Player {
  id: number;
  name: string;
}

export function CustomList({ players }: { players: Player[] }) {
  const table = useDataTable<Player>({
    columns: [{ accessorKey: "name", header: "Name" }],
    data: players,
    getRowId: (player) => String(player.id),
  });

  return (
    <FlatList
      data={table.getRowModel().flatRows}
      keyExtractor={(row) => row.id}
      renderItem={({ item }) => (
        <View>
          <Text>{item.original.name}</Text>
        </View>
      )}
    />
  );
}

Column widths

Columns with an explicit width are fixed; columns without one share the remaining space (flex: 1). Past minTableWidth, the table scrolls horizontally inside a ScrollView.

Advanced usage

Injecting FlashList

<DataTable /> renders rows through FlatList by default. For large data sets, pass FlashList from @shopify/flash-list via ListComponent — the package depends on neither FlatList internals nor FlashList directly, it just needs a component matching the VirtualListComponent shape:

import { DataTable } from "@vdnp/tablekit-react-native";
import type { Row, VirtualListComponent } from "@vdnp/tablekit-react-native";
import { FlashList } from "@shopify/flash-list";

interface Player {
  id: number;
  name: string;
}

export function BigRoster({ players }: { players: Player[] }) {
  return (
    <DataTable<Player>
      columns={[{ accessorKey: "name", header: "Name", sortable: true }]}
      data={players}
      getRowId={(player) => String(player.id)}
      ListComponent={FlashList as unknown as VirtualListComponent<Row<Player>>}
    />
  );
}

The as unknown as VirtualListComponent<Row<Player>> cast bridges FlashList's generic props to our minimal structural interface; it is safe because we only use data, renderItem, keyExtractor, extraData and ListEmptyComponent.

Server-side data

Same fetchData contract as core/web — the component shows an ActivityIndicator while loading and a native error view with a Retry button on failure:

import { DataTable } from "@vdnp/tablekit-react-native";
import type { FetchParams, FetchResult } from "@vdnp/tablekit-react-native";

interface Order {
  id: number;
  total: number;
}

async function fetchOrders(params: FetchParams): Promise<FetchResult<Order>> {
  const response = await fetch(
    `https://api.example.com/orders?page=${params.pageIndex}&size=${params.pageSize}`,
  );
  const body = (await response.json()) as { items: Order[]; total: number };
  return { rows: body.items, totalCount: body.total };
}

export function Orders() {
  return (
    <DataTable<Order>
      columns={[{ accessorKey: "id", header: "Order", sortable: true }]}
      fetchData={fetchOrders}
      getRowId={(order) => String(order.id)}
      onError={(error) => console.warn(error)}
    />
  );
}

Theming

Styling comes from @vdnp/tablekit-theme token objects — no StyleSheet overrides needed. Use the built-in dark palette or a custom token set:

import { DataTable } from "@vdnp/tablekit-react-native";
import { lightTheme } from "@vdnp/tablekit-theme";
import type { ThemeTokens } from "@vdnp/tablekit-theme";

const brand: ThemeTokens = {
  ...lightTheme,
  colors: { ...lightTheme.colors, accent: "#0ea5e9" },
};

export function Branded({ rows }: { rows: { id: number }[] }) {
  return (
    <DataTable
      columns={[{ accessorKey: "id", header: "ID" }]}
      data={rows}
      theme={brand}
    />
  );
}

colorScheme="dark" switches to the built-in dark tokens with no other setup.

Accessibility

Native accessibility maps to the same semantics as the web:

| Element | Native a11y | | --- | --- | | Selection checkbox | accessibilityRole="checkbox" + accessibilityState={{ checked }} | | Sortable header | accessibilityRole="button" + accessibilityState={{ selected }} | | Row (selectable) | accessibilityState={{ selected }} | | Group / expander | accessibilityRole="button" + accessibilityState={{ expanded }} | | Error view | accessibilityRole="alert" | | Pagination info | accessibilityLiveRegion="polite" |

Screen readers (VoiceOver / TalkBack) announce sort state, selection and page changes accordingly.

Testing

This is the only package that uses Jest instead of Vitest: React Native ships untranspiled Flow source that Vitest's esbuild resolver can't parse, so component tests run through the official react-native Jest preset. Everything is scoped to this package — the config never leaks to the rest of the monorepo.

# from the repo root
pnpm -F @vdnp/tablekit-react-native test        # jest: helper + component tests
pnpm -F @vdnp/tablekit-react-native test -- --watch

# or from packages/react-native
pnpm test

Tests use @testing-library/react-native. When adding tests, import test globals from @jest/globals (import { describe, it, expect, jest } from "@jest/globals";) so they type-check under the package's strict tsconfig (types: []). The whole suite (this package's Jest job and the other packages' Vitest job) runs as separate CI jobs.

Known limitations

  • Column resizing is web-only (no drag handle on native).
  • Horizontal scroll is required for wide tables; size columns with width and set minTableWidth to tune the breakpoint.
  • Doc snippets in this README aren't auto-compiled by pnpm docs:check: that checker resolves imports from the repo root, where react-native isn't installed (it's a dev dependency of this package only). The snippets are hand-verified; the component tests above are the real behavioral coverage.

API reference

Generated types: docs/api-reference/react-native.

<DataTable<TData> /> props

Extends TableOptions<TData> and adds: table, onRowPress, onRowLongPress, enableSelection, bulkActions / onBulkAction, showGlobalFilter, showPagination, renderLoading / renderEmpty / renderError, renderExpandedRow, theme / colorScheme, labels, ListComponent, minTableWidth.

Exports

useDataTable, DataTable, defaultLabels, cellLayout, formatCellText, createColumnHelper, the VirtualListComponent / VirtualListProps / CellLayout types, and the re-exported core types (ColumnDef, Row, Table, FetchParams, FetchResult, …).

MIT © TableKit