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

react-native-smart-table

v0.1.2

Published

Secure, performant, dark-theme-first React Native table with virtualization, frozen columns, row/col span, and composable custom cells.

Readme

react-native-smart-table

Enterprise-grade, dark-theme-first React Native table with virtualization, frozen columns, row/col span, sorting, selection, search, and inline cell editing.


Installation

npm install react-native-smart-table

Peer dependencies

npm install @shopify/flash-list react-native-reanimated @react-native-community/datetimepicker

Follow the native setup guides for each peer dependency:

Babel plugin — add the Reanimated plugin to your babel.config.js:

// Reanimated v3
plugins: ['react-native-reanimated/plugin']

// Reanimated v4 (uses react-native-worklets)
plugins: ['react-native-worklets/plugin']

Quick start

import { SmartTable } from 'react-native-smart-table';
import type { SmartTableColumn } from 'react-native-smart-table';

type User = { id: string; name: string; role: string; score: number };

const COLUMNS: SmartTableColumn<User>[] = [
  { key: 'name',  title: 'Name',  width: 160, frozen: 'left' },
  { key: 'role',  title: 'Role',  flex: 1,    minWidth: 100  },
  { key: 'score', title: 'Score', width: 80,  sortable: true },
];

const DATA: User[] = [
  { id: '1', name: 'Alice', role: 'Admin',  score: 95 },
  { id: '2', name: 'Bob',   role: 'Editor', score: 72 },
  { id: '3', name: 'Carol', role: 'Viewer', score: 58 },
];

export function MyScreen() {
  return (
    <SmartTable
      columns={COLUMNS}
      data={DATA}
      getRowId={(row, _index) => row.id}
    />
  );
}

Column definition (SmartTableColumn<T>)

| Prop | Type | Description | |---|---|---| | key | string | Column identifier | | title | string? | Header label | | width | number? | Fixed pixel width | | flex | number? | Proportional width (CSS flex) | | minWidth | number? | Width floor during flex distribution | | maxWidth | number? | Width ceiling during flex distribution | | frozen | 'left' \| 'right'? | Pin column to a panel | | sortable | boolean? | Show sort indicator; enable sort toggle | | accessor | (row: T) => unknown | Custom value extractor | | render | (value, row) => ReactNode | Simple cell renderer | | renderCell | (context) => ReactNode | Full-context renderer | | renderHeader | (context) => ReactNode | Custom header renderer | | dataType | 'string' \| 'number' \| 'date' \| 'boolean' \| 'custom' | Informs sort comparator |


Props (SmartTableProps<T>)

| Prop | Type | Default | Description | |---|---|---|---| | data | readonly T[] | — | Row data | | columns | readonly SmartTableColumn<T>[] | — | Column definitions | | getRowId | (row, index) => string | — | Required stable row identity | | defaultRowHeight | number | 48 | Row height in px | | stickyHeader | boolean | true | Pins header during scroll | | loading | boolean | false | Shows loading state | | error | unknown | — | Shows error state | | stateComponents | { loading, empty, error } | — | Custom state UIs | | selection | TableSelectionConfig<T> | — | Row selection (see below) | | sorting | TableSortingConfig<T> | — | Column sorting (see below) | | interaction | TableInteractionConfig<T> | — | Press handlers |


Sorting

import { useTableSorting } from 'react-native-smart-table';

function MyScreen() {
  const sorting = useTableSorting({
    data: DATA,
    columns: COLUMNS,
    enabled: true,
    // mode: 'server' — skips client sort, fires onSortModelChange
  });

  return (
    <SmartTable
      columns={COLUMNS}
      data={sorting.sortedData}
      getRowId={(row) => row.id}
      sorting={sorting}
    />
  );
}

Tap a sortable column header to cycle: unsorted → ascending ↑ → descending ↓ → unsorted.


Selection

import { useTableSelection } from 'react-native-smart-table';

function MyScreen() {
  const selection = useTableSelection({
    data: DATA,
    getRowId: (row) => row.id,
    enabled: true,
    multiple: true,
    onSelectionChange: (ids) => console.log([...ids]),
  });

  return (
    <SmartTable
      columns={COLUMNS}
      data={DATA}
      getRowId={(row) => row.id}
      selection={selection}
    />
  );
}

A checkbox column is automatically prepended when selection.enabled is true. The header renders a select-all checkbox with indeterminate state.


Search

import { useTableSearch } from 'react-native-smart-table';

function MyScreen() {
  const [query, setQuery] = useState('');

  const { filteredData, matchCount } = useTableSearch({
    data: DATA,
    columns: COLUMNS,
    query,
    searchableColumns: ['name', 'role'], // omit to search all columns
  });

  return (
    <>
      <TextInput value={query} onChangeText={setQuery} placeholder="Search…" />
      <SmartTable columns={COLUMNS} data={filteredData} getRowId={(r) => r.id} />
    </>
  );
}

Inline cell editing

import { useTableEditing, CellEditor } from 'react-native-smart-table';

const COLUMNS: SmartTableColumn<User>[] = [
  {
    key: 'name',
    title: 'Name',
    width: 160,
    renderCell: ({ value, row, rowIndex, columnKey }) => {
      const config = editing.getCellConfig(row, rowIndex, columnKey);

      if (editing.isCellEditing(row.id, columnKey) && config) {
        return (
          <CellEditor
            config={config}
            draftValue={editing.draftValue}
            validationError={editing.validationError}
            onDraftChange={editing.updateDraft}
            onCommit={editing.commitEdit}
            onCancel={editing.cancelEdit}
          />
        );
      }

      return (
        <TouchableOpacity onPress={() => editing.startEdit(row.id, columnKey, value, row, rowIndex)}>
          <Text>{String(value)}</Text>
        </TouchableOpacity>
      );
    },
  },
];

function MyScreen() {
  const [rows, setRows] = useState(DATA);

  const editing = useTableEditing<User>({
    getCellEditConfig: (_row, _idx, col) => {
      if (col === 'name')  return { inputType: 'text' };
      if (col === 'score') return { inputType: 'number', validate: v => Number(v) < 0 ? 'Must be positive' : null };
      if (col === 'role')  return { inputType: 'select', options: [{ label: 'Admin', value: 'Admin' }, { label: 'Editor', value: 'Editor' }] };
      return null;
    },
    onCellEditCommit: (rowId, columnKey, newValue) => {
      setRows(prev => prev.map(r => r.id === rowId ? { ...r, [columnKey]: newValue } : r));
    },
  });

  return <SmartTable columns={COLUMNS} data={rows} getRowId={(r) => r.id} />;
}

Supported inputType values: 'text', 'number', 'select', 'date'.


Frozen columns

Set frozen: 'left' or frozen: 'right' on any column. The table renders three panels — left, scrollable center, right — and synchronises vertical scroll across all three.

const COLUMNS: SmartTableColumn<Row>[] = [
  { key: 'name',    frozen: 'left',  width: 160 },
  { key: 'details', flex: 1 },
  { key: 'actions', frozen: 'right', width: 80  },
];

Row span / col span

Supply a getCellSpan prop to calculateTableLayout or pass it to the engine directly:

calculateTableLayout({
  data,
  columns,
  getRowId: (r) => r.id,
  availableWidth: 400,
  defaultRowHeight: 48,
  getCellSpan: (row, rowIndex, column) => {
    if (column.key === 'category' && rowIndex === 0) return { colSpan: 2 };
    return undefined;
  },
});

Theme

import { TableThemeProvider, createTableTheme, defaultDarkTheme } from 'react-native-smart-table';

const theme = createTableTheme(defaultDarkTheme, {
  colors: { primary: '#7C3AED' },
});

<TableThemeProvider theme={theme}>
  <SmartTable ... />
</TableThemeProvider>

Layout engine (headless)

The layout engine is exported as a pure function for headless or custom-renderer use:

import { calculateTableLayout } from 'react-native-smart-table';

const layout = calculateTableLayout({
  data,
  columns,
  getRowId: (r) => r.id,
  availableWidth: 800,
  defaultRowHeight: 48,
});

// layout.columns — resolved pixel widths and x offsets
// layout.rows    — resolved y offsets and heights
// layout.cells   — pixel-positioned cell geometry (x, y, width, height)
// layout.frozenColumns — { left, center, right }

License

MIT © SaiSahitya