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

@maxpoletto/sortable-table

v1.3.0

Published

A reusable, configurable table component with sorting, filtering, and pagination

Readme

SortableTable

A reusable and configurable sortable table component with good pagination and theming support.

Features

Sorting

  • Multi-type sorting (string, number, date, boolean), ascending/descending order
  • Visual sort indicators with smooth transitions

Navigation

  • Navigation controls: First (<<), Previous (<), Next (>), Last (>>) buttons, disabled when not applicable
  • Editable page number: click the current page number to jump to any page, press Enter to confirm, Escape to cancel
  • Configurable page sizes
  • Localizable page indicator via the pageInfo template option

Customization

  • CSS custom properties for easy theming
  • A few built-in themes (compact, spacious, minimal, bordered, solid)
  • Custom formatter functions
  • HTML-safe rendering by default, with explicit trusted HTML opt-in
  • Responsive design with mobile-optimized layouts

Efficiency

  • Memory efficient: data is represented as an array of arrays
  • Limited DOM manipulation (only updates visible rows)

Quick Start

<!DOCTYPE html>
<html>
<head>
    <link rel="stylesheet" href="sortable-table.css">
</head>
<body>
    <div id="my-table"></div>
    
    <script src="sortable-table.js"></script>
    <script>
        const columns = [
            { key: 'id', label: 'ID', type: 'number' },
            { key: 'name', label: 'Name', type: 'string' },
            { key: 'salary', label: 'Salary', type: 'number', 
              formatter: (value) => `$${value.toLocaleString()}` },
            { key: 'startDate', label: 'Start Date', type: 'date' }
        ];

        const data = [
            [1, 'John Doe', 75000, new Date('2020-01-15')],
            [2, 'Jane Smith', 85000, new Date('2019-03-20')]
        ];

        const table = new SortableTable({
            container: '#my-table',
            data: data,
            columns: columns,
            rowsPerPage: 25
        });
    </script>
</body>
</html>

Configuration Options

Constructor

const table = new SortableTable({
    // Required
    container: '#table-container',     // Element or selector
    data: [...],                       // Array of arrays (one array per row)
    columns: [...],                    // Column definitions (see below)

    // Optional
    rowsPerPage: 25,                   // Rows per page (default: 25)
    showPagination: true,              // Show pagination controls (default: true)
    allowSorting: true,                // Allow column sorting (default: true)
    cssPrefix: 'sortable-table',       // CSS class prefix (default: 'sortable-table')
    emptyMessage: 'No data available', // Message when no data (default: 'No data available')
    pageInfo: 'Page {current} of {total}', // Pagination text; include both
                                       // {current}/{total}, each replaced once
                                       // with the page-number controls
                                       // (default: 'Page {current} of {total}')
    sort: { column: 'name', ascending: true }, // Initial sort

    // Event callbacks
    onSort: (column, ascending) => {...},        // Called when sorted
    onPageChange: (page, totalPages) => {...},   // Called when page changes
    onRowClick: (data, index, event) => {...}    // Called when row is clicked
});

Column Definition

const columns = [
    {
        key: 'fieldName',              // Data field name or function
        label: 'Display Name',         // Column header text
        type: 'string',                // 'string', 'number', 'date', 'boolean'
        width: '120px',                // Optional: column width
        hidden: false,                 // Optional: hide column (default: false)
        sortable: true,                // Optional: allow sorting (default: true)
        className: 'custom-class',     // Optional: custom header CSS class
        cellClassName: 'cell-class',   // Optional: custom cell CSS class
        formatter: (value, row) => {   // Optional: custom formatter function
            return `$${value.toLocaleString()}`;
        },
        trustedHTML: false             // Optional: render formatter output as
                                       // raw HTML only when explicitly true
    }
];

Formatter return values are escaped and rendered as text by default. Set trustedHTML: true only for columns whose formatter returns application-controlled markup, such as action buttons or icons. Do not enable it for user-entered data.

API Methods

Data Management

// Update all data
table.setData(newDataArray);

// Add a single row
table.addRow(rowData);

// Remove rows that match the predicate
table.removeRows(predicate);

// Sort by column in ascending or descending order
table.sort(key, type, ascending);

// Toggle sort order by column
// (if key denotes the current sort column, else sort by that column in ascending order)
table.toggleSort(key, type);

// Filter data (creates a view, doesn't modify original)
table.filter(row => row.salary > 50000);

// Clear all filters
table.clearFilter();

Navigation

// Go to specific page
table.goToPage(pageNumber);

// Get current page data
const currentData = table.getVisibleData();

Cleanup

// Destroy table and clean up event listeners
table.destroy();

Theming

CSS Custom Properties

:root {
    --st-primary-color: #2196F3;     /* Accent color */
    --st-border-color: #ddd;         /* Border color */
    --st-header-bg: #f7f7f7;         /* Header background */
    --st-hover-bg: #f9f9f9;          /* Row hover background */
    --st-font-size: 13px;            /* Base font size */
    --st-padding: 8px;               /* Base padding */
    --st-border-radius: 4px;         /* Border radius for buttons and container,  */
}

Built-in Theme Classes

<!-- Compact theme -->
<div id="table" class="theme-compact"></div>

<!-- Spacious theme -->
<div id="table" class="theme-spacious"></div>

<!-- Minimal theme (no borders) -->
<div id="table" class="theme-minimal"></div>

<!-- Bordered cells -->
<div id="table" class="theme-bordered"></div>

<!-- Solid rows (no striping) -->
<div id="table" class="theme-solid"></div>

Examples: a basic employee table

const employees = [
    [1, 'Alice Johnson', 'Engineering', 95000, true],
    [2, 'Bob Smith', 'Marketing', 75000, true]
];

const table = new SortableTable({
    container: '#employee-table',
    data: employees,
    columns: [
        { key: 'id', label: 'ID', type: 'number', width: '60px' },
        { key: 'name', label: 'Name', type: 'string' },
        { key: 'department', label: 'Department', type: 'string' },
        { key: 'salary', label: 'Salary', type: 'number', 
          formatter: (value) => `$${value.toLocaleString()}` },
        { key: 'active', label: 'Status', type: 'boolean',
          formatter: (value) => value ? '✅ Active' : '❌ Inactive' }
    ],
    rowsPerPage: 20,
    onRowClick: (employee) => {
        alert(`Employee: ${employee[1]}\nSalary: $${employee[3].toLocaleString()}`);
    }
});

Browser Support

Tested on modern versions of Chrome, Firefox, Safari, and Edge. Technically should work with:

  • Chrome 60+
  • Firefox 55+
  • Safari 12+
  • Edge 79+

Files

To run demo or tests, run make test-server and open http://localhost:8000 in your browser.