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

flexi-datatable

v1.0.0

Published

Lightweight React DataTable with search, filtering, sorting, pagination, column toggle, CSV export and theming

Readme

flexi-datatable

Lightweight, plug-and-play React DataTable with built-in search, sorting, pagination, column toggling, CSV export, mass delete, drag-reorder, and theming. Zero configuration needed -- just pass your columns and data.

Installation

npm install flexi-datatable

Step 1: Import the Component and CSS

import { DataTable } from "flexi-datatable";
import "flexi-datatable/style.css";

The CSS import is required for the table to render correctly. Import it once in your app entry point or in the component where you use DataTable.

Step 2: Define Your Columns

Columns tell the table what headers to show and which field in your data each column maps to.

const columns = [
  { header: "Name", accessor: "name", sortable: true },
  { header: "Age", accessor: "age", sortable: true },
  { header: "City", accessor: "city" },
];

Each column object supports:

| Key | Type | Required | Description | | --- | --- | --- | --- | | header | string | Yes | Text shown in the table header | | accessor | string | Yes | The key in your data objects that this column displays | | sortable | bool | No | If true, clicking the header toggles ascending/descending sort | | render | func | No | Custom renderer (cellValue, rowObject) => JSX for full control |

Step 3: Prepare Your Data

Pass an array of objects. Each object is one row. The keys must match the accessor values in your columns.

const data = [
  { name: "Siva", age: 28, city: "Chennai" },
  { name: "Tamil", age: 24, city: "Madurai" },
  { name: "Devi", age: 30, city: "Pudukkottai" },
  { name: "John", age: 35, city: "London" },
  { name: "Sara", age: 22, city: "Paris" },
];

Step 4: Render the DataTable

Minimal Example

Only columns and data are required. Everything else is optional.

function App() {
  return <DataTable columns={columns} data={data} />;
}

Full-Featured Example

Enable all features by passing boolean props:

import { useState } from "react";
import { DataTable } from "flexi-datatable";
import "flexi-datatable/style.css";

function App() {
  const [data, setData] = useState([
    { id: 1, name: "Siva", age: 28, city: "Chennai" },
    { id: 2, name: "Tamil", age: 24, city: "Madurai" },
    { id: 3, name: "Devi", age: 30, city: "Pudukkottai" },
    { id: 4, name: "John", age: 35, city: "London" },
    { id: 5, name: "Sara", age: 22, city: "Paris" },
  ]);

  const columns = [
    { header: "Name", accessor: "name", sortable: true },
    { header: "Age", accessor: "age", sortable: true },
    { header: "City", accessor: "city" },
    {
      header: "Action",
      accessor: "id",
      render: (value, row) => (
        <div style={{ display: "flex", gap: "8px" }}>
          <button onClick={() => alert(`Viewing ${row.name}`)}>View</button>
          <button onClick={() => handleDelete(row)}>Delete</button>
        </div>
      ),
    },
  ];

  const handleDelete = (row) => {
    setData((prev) => prev.filter((r) => r.id !== row.id));
  };

  const handleMassDelete = (selectedRows) => {
    const selectedIds = selectedRows.map((r) => r.id);
    setData((prev) => prev.filter((r) => !selectedIds.includes(r.id)));
  };

  const handlePageChange = ({ page, rowsPerPage }) => {
    console.log(`Page: ${page}, Rows per page: ${rowsPerPage}`);
  };

  return (
    <DataTable
      columns={columns}
      data={data}
      theme="default"
      perPage={10}
      isSearch
      isPagination
      isCSV
      isColumnToggle
      isMassDelete
      isRowPerPage
      onDelete={handleMassDelete}
      onPageChange={handlePageChange}
    />
  );
}

Props Reference

| Prop | Type | Default | Description | | --- | --- | --- | --- | | columns | Array | [] | Required. Array of column definition objects (see above) | | data | Array | [] | Required. Array of row objects | | page | number | 1 | Initial page number | | perPage | number | 10 | Number of rows displayed per page | | theme | string | "default" | Built-in theme name: "default", "dark", "blue", "minimal" | | customTheme | object | {} | Inline style overrides for each part of the table (see Theming) | | isSearch | bool | false | Show the search input above the table | | isPagination | bool | false | Show pagination controls below the table | | isCSV | bool | false | Show the "Export CSV" button | | isColumnToggle | bool | false | Show the column visibility dropdown | | isMassDelete | bool | false | Show the "Mass Delete" button and row checkboxes | | isRowPerPage | bool | false | Show the rows-per-page dropdown selector | | onPageChange | func | - | Called when the page or rows-per-page changes. Receives { page: number, rowsPerPage: number } | | onDelete | func | - | Called when "Mass Delete" is confirmed. Receives an array of the selected row objects |

Feature Examples

Search

Enable the search bar to filter rows across all columns:

<DataTable columns={columns} data={data} isSearch />

Users can type any text and the table instantly filters to show only matching rows.

Sorting

Add sortable: true to any column definition. Users click the header to sort:

const columns = [
  { header: "Name", accessor: "name", sortable: true },
  { header: "Age", accessor: "age", sortable: true },
  { header: "City", accessor: "city" }, // not sortable
];
  • First click: sorts ascending (▲)
  • Second click: sorts descending (▼)
  • Unsorted columns show (↕)

Pagination

<DataTable
  columns={columns}
  data={data}
  isPagination
  perPage={5}
  onPageChange={({ page, rowsPerPage }) => {
    console.log(`Now on page ${page}, showing ${rowsPerPage} rows`);
  }}
/>

Add isRowPerPage to let users pick 5, 10, 20, or 50 rows per page:

<DataTable columns={columns} data={data} isPagination isRowPerPage perPage={10} />

Custom Cell Rendering

Use the render function in a column definition to render custom JSX in each cell. The function receives (cellValue, rowObject):

const columns = [
  { header: "Name", accessor: "name" },
  {
    header: "Status",
    accessor: "isActive",
    render: (value) => (
      <span style={{ color: value ? "green" : "red" }}>
        {value ? "Active" : "Inactive"}
      </span>
    ),
  },
  {
    header: "Actions",
    accessor: "id",
    render: (id, row) => (
      <button onClick={() => navigate(`/users/${id}`)}>
        View {row.name}
      </button>
    ),
  },
];

CSV Export

<DataTable columns={columns} data={data} isCSV />

Clicking "Export CSV" downloads a .csv file containing the currently filtered data (respects the active search query).

Column Toggle (Show/Hide Columns)

<DataTable columns={columns} data={data} isColumnToggle />

A "Select Columns" dropdown appears. Users can check/uncheck columns to show or hide them.

Column Drag Reorder

Columns are draggable by default. Users can drag-and-drop column headers to reorder them. No extra prop needed.

Mass Delete

<DataTable
  columns={columns}
  data={data}
  isMassDelete
  onDelete={(selectedRows) => {
    // selectedRows is an array of the row objects the user checked
    console.log("Rows to delete:", selectedRows);
    setData((prev) =>
      prev.filter((row) => !selectedRows.includes(row))
    );
  }}
/>

Each row gets a checkbox. A "Select All" checkbox appears in the header. Clicking "Mass Delete" triggers a confirmation dialog, then calls onDelete with the selected rows.

Theming

Built-in Themes

<DataTable theme="default" ... />  // light, clean
<DataTable theme="dark" ... />     // dark background, white text
<DataTable theme="blue" ... />     // blue header, blue accents
<DataTable theme="minimal" ... />  // borderless, minimal style

Custom Theme (Inline Style Overrides)

Pass a customTheme object to override styles on specific parts of the table. Every key is optional -- only include what you want to override:

const customTheme = {
  container: { background: "#1a1a2e", padding: "20px", borderRadius: "8px" },
  table: { border: "1px solid #333" },
  header: { background: "#16213e", color: "#e94560", fontWeight: "bold" },
  headerRow: { background: "#0f3460" },
  row: { background: "#1a1a2e" },
  cell: { padding: "12px", borderBottom: "1px solid #333" },
  searchBar: { background: "#16213e", color: "#fff", padding: "8px 12px" },
  csvButton: { background: "#e94560", color: "#fff", borderRadius: "4px" },
  deleteButton: { background: "#c0392b", color: "#fff" },
  pagiContainer: { justifyContent: "center" },
  prevButton: { background: "#16213e", color: "#fff" },
  nextButton: { background: "#16213e", color: "#fff" },
  pageButton: { background: "#16213e", color: "#fff" },
  columnToggle: {},
  columnBtn: { background: "#16213e", color: "#fff" },
  columnDropdown: { background: "#1a1a2e", border: "1px solid #333" },
  columnLabel: { color: "#fff" },
  columnInput: {},
};

<DataTable columns={columns} data={data} customTheme={customTheme} />

Using with API Data

import { useState, useEffect } from "react";
import { DataTable } from "flexi-datatable";
import "flexi-datatable/style.css";

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

  useEffect(() => {
    fetch("https://jsonplaceholder.typicode.com/users")
      .then((res) => res.json())
      .then((data) => {
        setUsers(data);
        setLoading(false);
      });
  }, []);

  const columns = [
    { header: "ID", accessor: "id", sortable: true },
    { header: "Name", accessor: "name", sortable: true },
    { header: "Email", accessor: "email" },
    { header: "Phone", accessor: "phone" },
    {
      header: "Website",
      accessor: "website",
      render: (url) => (
        <a href={`https://${url}`} target="_blank" rel="noreferrer">
          {url}
        </a>
      ),
    },
  ];

  if (loading) return <p>Loading...</p>;

  return (
    <DataTable
      columns={columns}
      data={users}
      theme="blue"
      perPage={5}
      isSearch
      isPagination
      isCSV
      isColumnToggle
      isRowPerPage
    />
  );
}

All Exports

You can also import individual sub-components and utilities:

import {
  DataTable,      // Main table component
  Pagination,     // Standalone pagination control
  SearchBar,      // Standalone search input
  ColumnToggle,   // Standalone column visibility dropdown
  exportCSV,      // Utility function: exportCSV(data, filename?)
  useTable,       // Hook: returns { filteredData, search, setSearch, sortColumn, sortField, sortOrder }
} from "flexi-datatable";

License

MIT