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

@sachithw/table-kit

v0.1.18

Published

A reusable React + TypeScript table component with zero-config dummy data and full customization

Readme

table-kit

A reusable React + TypeScript table component with zero-config dummy data and full customization. Perfect for building admin dashboards, data tables, and user interfaces that match modern design patterns.

npm version TypeScript MIT License

✨ Features

  • Zero-config: Works out of the box with dummy data
  • TypeScript-first: Full type safety and IntelliSense support
  • Customizable: Fully configurable columns, actions, and styling
  • Responsive: Mobile-friendly design with CSS modules
  • Accessible: ARIA labels, keyboard navigation, and focus management
  • Animated: Smooth transitions with optional framer-motion support
  • Lightweight: No external UI dependencies

🚀 Quick Start

Installation

npm install @sachithw/table-kit
# or
pnpm add @sachithw/table-kit
# or
yarn add @sachithw/table-kit

Zero-config Usage

The simplest way to get started:

import { TableKit } from "@sachithw/table-kit";

export default function App() {
  return <TableKit />;
}

This renders a beautiful table with dummy user data, complete with avatars, access chips, and action buttons.

📖 API Reference

Core Types

type Column<T> = {
  id?: string;
  header: string | React.ReactNode;
  accessorKey?: keyof T & string;
  cell?: (value: unknown, row: T) => React.ReactNode;
  width?: number | string;
  sortable?: boolean; // reserved for future use
};

type TableAction<T> = {
  id: string;
  label: string;
  icon?: React.ReactNode;
  onClick: (row: T) => void;
  isDanger?: boolean;
  disabled?: boolean;
  show?: (row: T) => boolean;
};

type TableKitProps<T> = {
  // Data
  data?: T[];
  columns?: Column<T>[];
  getRowId?: (row: T) => string;

  // Actions
  actions?: TableAction<T>[];
  overrideActions?: TableAction<T>[];
  onEdit?: (row: T) => void;
  onView?: (row: T) => void;
  onDelete?: (row: T) => void;

  // Header & composition
  title?: React.ReactNode;
  renderHeaderLeft?: () => React.ReactNode;
  renderHeaderRight?: () => React.ReactNode;
  renderHeaderExtras?: () => React.ReactNode;

  // States & visuals
  loading?: boolean;
  emptyState?: React.ReactNode;
  className?: string;
};

Default UserRow Interface

type UserRow = {
  id: string;
  name: string;
  email: string;
  access: string[];
  lastActive: string | Date;
  dateAdded: string | Date;
  avatarUrl?: string;
};

🎯 Usage Examples

1. With API Data

import { TableKit, Column, UserRow } from "@sachithw/table-kit";

const users: UserRow[] = [
  {
    id: "1",
    name: "John Doe",
    email: "[email protected]",
    access: ["Admin", "Editor"],
    lastActive: "2024-03-15",
    dateAdded: "2024-01-10",
    avatarUrl: "https://example.com/avatar.jpg",
  },
  // ... more users
];

export default function UsersTable() {
  return (
    <TableKit
      data={users}
      title="Team Members"
      onEdit={(user) => console.log("Edit", user)}
      onDelete={(user) => console.log("Delete", user)}
    />
  );
}

2. Custom Columns

import { TableKit, Column } from "@sachithw/table-kit";

type Product = {
  id: string;
  name: string;
  price: number;
  category: string;
  inStock: boolean;
};

const columns: Column<Product>[] = [
  {
    id: "name",
    header: "Product Name",
    accessorKey: "name",
  },
  {
    id: "price",
    header: "Price",
    accessorKey: "price",
    cell: (value) => `$${(value as number).toFixed(2)}`,
  },
  {
    id: "category",
    header: "Category",
    accessorKey: "category",
  },
  {
    id: "status",
    header: "Status",
    accessorKey: "inStock",
    cell: (value) => (
      <span className={value ? "text-green-600" : "text-red-600"}>
        {value ? "In Stock" : "Out of Stock"}
      </span>
    ),
  },
];

export default function ProductsTable() {
  return (
    <TableKit<Product> data={products} columns={columns} title="Products" />
  );
}

3. Custom Actions

import { TableKit, TableAction } from "@sachithw/table-kit";

const customActions: TableAction<UserRow>[] = [
  {
    id: "impersonate",
    label: "Impersonate",
    icon: "👤",
    onClick: (user) => {
      // Impersonate user logic
      window.location.href = `/admin/impersonate/${user.id}`;
    },
    show: (user) => user.access.includes("Admin"), // Only show for admins
  },
  {
    id: "sendEmail",
    label: "Send Email",
    icon: "✉️",
    onClick: (user) => {
      window.location.href = `mailto:${user.email}`;
    },
  },
];

export default function AdminUsersTable() {
  return (
    <TableKit
      title="All Users"
      actions={customActions} // Appends to default actions
      onEdit={(user) => openEditModal(user)}
      onDelete={(user) => confirmDelete(user)}
    />
  );
}

4. Header Slots & Composition

import { TableKit } from "@sachithw/table-kit";

function SearchInput() {
  return (
    <input
      type="text"
      placeholder="Search users..."
      className="px-3 py-2 border rounded-md"
    />
  );
}

function FilterButton() {
  return <button className="px-4 py-2 border rounded-md">🔍 Filters</button>;
}

function AddUserButton() {
  return (
    <button className="px-4 py-2 bg-blue-600 text-white rounded-md">
      + Add User
    </button>
  );
}

export default function UsersWithHeader() {
  return (
    <TableKit
      title="All users 44"
      renderHeaderLeft={() => <SearchInput />}
      renderHeaderRight={() => (
        <>
          <FilterButton />
          <AddUserButton />
        </>
      )}
    />
  );
}

5. Loading & Empty States

import { TableKit } from "@sachithw/table-kit";

export default function LoadingTable() {
  const [loading, setLoading] = useState(true);
  const [users, setUsers] = useState([]);

  useEffect(() => {
    fetchUsers().then((data) => {
      setUsers(data);
      setLoading(false);
    });
  }, []);

  return (
    <TableKit
      data={users}
      loading={loading}
      emptyState={
        <div className="text-center py-8">
          <p>No users found</p>
          <button className="mt-2 px-4 py-2 bg-blue-600 text-white rounded">
            Invite Users
          </button>
        </div>
      }
    />
  );
}

6. Override Default Actions

import { TableKit, TableAction } from "@sachithw/table-kit";

const customActionsOnly: TableAction<UserRow>[] = [
  {
    id: "invite",
    label: "Invite",
    onClick: (user) => sendInvite(user),
  },
  {
    id: "archive",
    label: "Archive",
    isDanger: true,
    onClick: (user) => archiveUser(user),
  },
];

export default function CustomActionsTable() {
  return (
    <TableKit
      overrideActions={customActionsOnly} // Replaces all default actions
    />
  );
}

🎨 Theming & Customization

CSS Variables

table-kit uses CSS custom properties for easy theming:

:root {
  /* Spacing */
  --tk-spacing-xs: 0.25rem;
  --tk-spacing-sm: 0.5rem;
  --tk-spacing-md: 0.75rem;
  --tk-spacing-lg: 1rem;
  --tk-spacing-xl: 1.5rem;

  /* Colors */
  --tk-bg: #ffffff;
  --tk-bg-hover: #f3f4f6;
  --tk-border: #e5e7eb;
  --tk-text-primary: #111827;
  --tk-text-secondary: #6b7280;

  /* Chip colors */
  --tk-chip-bg: #f3f4f6;
  --tk-chip-text: #374151;

  /* Accent colors */
  --tk-accent-blue: #3b82f6;
  --tk-accent-green: #10b981;
  --tk-accent-red: #ef4444;
}

Custom Theme Example

/* Custom dark theme */
.dark-theme {
  --tk-bg: #1f2937;
  --tk-bg-hover: #374151;
  --tk-border: #4b5563;
  --tk-text-primary: #f9fafb;
  --tk-text-secondary: #d1d5db;
  --tk-chip-bg: #374151;
  --tk-chip-text: #f3f4f6;
}
<TableKit className="dark-theme" />

🎬 Animations

table-kit supports smooth animations with optional framer-motion integration:

With framer-motion (recommended)

npm install framer-motion
import { TableKit } from "@sachithw/table-kit";
// framer-motion will be automatically detected and used

CSS-only animations

If framer-motion is not installed, table-kit falls back to CSS animations:

@media (prefers-reduced-motion: reduce) {
  /* Animations automatically disabled for users who prefer reduced motion */
}

🧪 Testing

Basic unit tests for key functionality:

import { render, screen } from "@testing-library/react";
import { TableKit } from "@sachithw/table-kit";

test("renders dummy data by default", () => {
  render(<TableKit />);
  expect(screen.getByText(/All users/)).toBeInTheDocument();
});

test("renders custom data", () => {
  const data = [{ id: "1", name: "Test User", email: "[email protected]" }];
  render(<TableKit data={data} />);
  expect(screen.getByText("Test User")).toBeInTheDocument();
});

📦 Bundle Analysis

  • ESM: ~15KB gzipped
  • CJS: ~16KB gzipped
  • Dependencies: React 18+ (peer)
  • Optional: framer-motion (peer)

🤝 Contributing

  1. Fork the repository
  2. Create your feature branch (git checkout -b feature/amazing-feature)
  3. Commit your changes (git commit -m 'Add some amazing feature')
  4. Push to the branch (git push origin feature/amazing-feature)
  5. Open a Pull Request

📄 License

MIT © Sachith Wickramasekara

🔗 Links