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

fish-table-lib

v0.1.3

Published

Generic, reusable DataTable component built with React, TypeScript, shadcn/ui and TanStack Table

Readme

fish-table-lib

Generic, reusable data table built with React + TypeScript, shadcn/ui for styling, and @tanstack/react-table as the data engine.

Demo: fish-table-page.vercel.app

Versión en español

Features

  • Generic DataTable<TData, TValue> component with full TypeScript support.
  • Built-in loading and empty (emptyMessage) states.
  • Client-side (automatic) or server-side (controlled) pagination.
  • Column sorting (opt-in with enableSorting: true).
  • Clickable rows via onRowClick.
  • shadcn/ui styling (Tailwind CSS v4 + theme CSS variables).
  • Also exports Table, TableHeader, TableRow, etc. primitives and TanStack's createColumnHelper / ColumnDef so you don't need a separate @tanstack/react-table install for column definitions.

Installation

npm install fish-table-lib

With yarn or pnpm:

yarn add fish-table-lib
pnpm add fish-table-lib

Consumer project requirements

  • React 18+.
  • Tailwind CSS v4 with a shadcn/ui theme (CSS variables: --background, --foreground, --muted, --primary, --border, etc.).
  • Tell Tailwind to scan the library so it generates the utility classes. In your global CSS:
@import 'tailwindcss';

@source '../node_modules/fish-table-lib/src';

(adjust the relative path based on where your CSS file lives).

Basic usage

import { DataTable, type ColumnDef } from 'fish-table-lib'

interface User {
  id: string
  name: string
  email: string
  role: string
}

const columns: ColumnDef<User>[] = [
  {
    accessorKey: 'name',
    header: 'Name',
    enableSorting: true, // enable sorting on this column
  },
  {
    accessorKey: 'email',
    header: 'Email',
  },
  {
    accessorKey: 'role',
    header: 'Role',
    cell: ({ row }) => <span className="font-medium">{row.original.role}</span>,
  },
]

export function UsersTable({ users }: { users: User[] }) {
  return <DataTable columns={columns} data={users} />
}

Props

| Prop | Type | Default | Description | | -------------- | --------------------------------------------- | -------------------- | -------------------------------------------------------------------- | | columns | ColumnDef<TData, TValue>[] | — (required) | TanStack Table column definitions. | | data | TData[] | — (required) | Rows to render. | | loading | boolean | false | Shows a loading row with a spinner. | | emptyMessage | React.ReactNode | 'No hay resultados.' | Content shown when there are no rows. | | pagination | boolean \| DataTablePaginationConfig | undefined | true = client-side; object = server-side; omitted = no pagination. | | onRowClick | (row: TData, tableRow: Row<TData>) => void | undefined | Row click handler (adds cursor-pointer). | | className | string | undefined | Extra classes for the root container. |

Examples

Loading and empty state

<DataTable
  columns={columns}
  data={users}
  loading={isLoading}
  emptyMessage="No users found"
/>

Client-side pagination

TanStack handles pagination internally (10 rows per page by default):

<DataTable columns={columns} data={users} pagination />

Server-side pagination (controlled)

You control state and fetch per page. pageIndex is 0-based:

import { DataTable, type DataTablePaginationConfig } from 'fish-table-lib'

function UsersTable() {
  const [pageIndex, setPageIndex] = useState(0)
  const pageSize = 20

  const { users, total, loading } = useUsers({ page: pageIndex + 1, limit: pageSize })

  const pagination: DataTablePaginationConfig = {
    pageIndex,
    pageSize,
    total, // total rows on the server
    onPageChange: setPageIndex,
  }

  return (
    <DataTable
      columns={columns}
      data={users}
      loading={loading}
      pagination={pagination}
    />
  )
}

Clickable rows

<DataTable
  columns={columns}
  data={users}
  onRowClick={user => router.push(`/users/${user.id}`)}
/>

Columns with createColumnHelper (stricter typing)

import { DataTable, createColumnHelper } from 'fish-table-lib'

const helper = createColumnHelper<User>()

const columns = [
  helper.accessor('name', { header: 'Name', enableSorting: true }),
  helper.accessor('email', { header: 'Email' }),
  helper.display({
    id: 'actions',
    header: '',
    cell: ({ row }) => <button onClick={() => edit(row.original)}>Edit</button>,
  }),
]

Action column without triggering onRowClick

Stop propagation on the cell content:

helper.display({
  id: 'actions',
  cell: ({ row }) => (
    <button onClick={e => { e.stopPropagation(); remove(row.original.id) }}>
      Delete
    </button>
  ),
})

Sorting

Sorting is disabled by default on all columns. Enable it per column with enableSorting: true; the header becomes clickable and shows sort indicators (▲ / ▼ / ↕).

Library structure

fish-table-lib/
├── src/
│   ├── components/
│   │   ├── DataTable/
│   │   │   ├── DataTable.tsx   # Main component + paginator
│   │   │   ├── types.ts        # DataTableProps, DataTablePaginationConfig
│   │   │   └── index.ts
│   │   └── ui/
│   │       └── table.tsx       # shadcn/ui primitives (Table, TableRow, ...)
│   ├── lib/
│   │   └── utils.ts            # cn() (clsx + tailwind-merge)
│   └── index.ts                # Public entry point
├── dist/                       # Build output (ESM + CJS + .d.ts)
├── package.json
├── tsconfig.json
├── tsup.config.ts
└── README.md

Scripts

npm run build       # Build to dist/ (ESM + CJS + types)
npm run dev         # Watch mode
npm run type-check  # tsc --noEmit

Notes

  • The bundle includes 'use client', so it works out of the box with Next.js App Router.
  • react and react-dom are peerDependencies — your project supplies the version.

Local installation (development)

If you're working on the library repo itself:

cd fish-table
npm install
npm run build
npm link   # optional: link globally

In your consumer project:

npm link fish-table-lib
# or
npm install ../fish-table