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

@multiplatform.one/table

v6.1.0

Published

Advanced table components with filtering, pagination, sorting, and URL state management

Readme

@multiplatform.one/table

Note: @multiplatform.one/router is optional - works with any React router or framework!

npm version License

🎯 Key Features

  • 🔍 Advanced Filtering - query builder with 20+ operators
  • 🔗 URL State Management - Bookmarkable/shareable table states
  • 🌍 Universal Compatibility - Works with any React router or framework
  • 📱 Cross-Platform - Web, React Native, Tauri, and more
  • ⚡ Performance - Server/client-side filtering and pagination
  • 🎨 Beautiful UI - Built with Tamagui for consistent design
  • ♿ Accessible - WCAG 2.1 AA compliant
  • 🔧 Type-Safe - Full TypeScript support
  • 📦 Modular - Tree-shakeable components

🚀 Quick Start

# For OneStack apps
pnpm add @multiplatform.one/table @multiplatform.one/router

# For other React apps
pnpm add @multiplatform.one/table

# For React Router apps
pnpm add @multiplatform.one/table react-router-dom

# For Next.js apps
pnpm add @multiplatform.one/table next
import { DataTable, createUrlStrategy } from "@multiplatform.one/table";

function UserTable() {
  const data = [
    { id: 1, name: "John", email: "[email protected]", role: "admin" },
    { id: 2, name: "Jane", email: "[email protected]", role: "user" },
  ];

  const columns = [
    { accessorKey: "name", header: "Name" },
    { accessorKey: "email", header: "Email" },
    { accessorKey: "role", header: "Role" },
  ];

  return (
    <DataTable
      data={data}
      columns={columns}
      syncWithUrl={true} // Enable URL state management
      enableFiltering={true}
      enableSorting={true}
      enablePagination={true}
      // urlStrategy: optional - defaults to OneStack, or use createUrlStrategy for other routers
    />
  );
}

📦 Package Boundaries

| Package | Responsibility | | ------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | @multiplatform.one/table | Framework-agnostic table: display, filter, sort, paginate, virtualize, resize, pin, expand, keyboard nav. No Frappe or API assumptions. | | @multiplatform.one/frappe-ui | Frappe integration: FrappeTable wraps DataTable, fetches from Frappe, maps to DataTable props. Uses manualFiltering, manualPagination, manualSorting when serverSide is enabled. |

Dependency direction: frappe-ui → table. Table does not depend on frappe-ui or frappe.


🌍 Cross-Platform Compatibility

Works with any React framework and all platforms:

| Framework/Router | URL Strategy | Status | | ---------------------- | --------------------------------- | --------------- | | OneStack | Automatic (default) | ✅ Full Support | | React Router | createUrlStrategy.reactRouter() | ✅ Full Support | | Next.js App Router | createUrlStrategy.nextJs() | ✅ Full Support | | React Native | syncWithUrl={false} | ✅ Full Support | | Tauri | createUrlStrategy.universal() | ✅ Full Support | | Vanilla React | createUrlStrategy.universal() | ✅ Full Support | | Any Router | createUrlStrategy.custom() | ✅ Full Support |

React Router Integration

import { useSearchParams } from "react-router-dom";
import { DataTable, createUrlStrategy } from "@multiplatform.one/table";

function MyTable() {
  const [searchParams, setSearchParams] = useSearchParams();
  const urlStrategy = createUrlStrategy.reactRouter(() => [searchParams, setSearchParams], 500);

  return <DataTable data={data} columns={columns} syncWithUrl={true} urlStrategy={urlStrategy} />;
}

Next.js App Router Integration

import { useSearchParams, useRouter } from "next/navigation";
import { DataTable, createUrlStrategy } from "@multiplatform.one/table";

function MyTable() {
  const searchParams = useSearchParams();
  const router = useRouter();
  const urlStrategy = createUrlStrategy.nextJs(
    () => searchParams,
    () => router,
    500,
  );

  return <DataTable data={data} columns={columns} syncWithUrl={true} urlStrategy={urlStrategy} />;
}

Universal Strategy (Works Everywhere)

import { DataTable, createUrlStrategy } from "@multiplatform.one/table";

function MyTable() {
  const urlStrategy = createUrlStrategy.universal(500);

  return <DataTable data={data} columns={columns} syncWithUrl={true} urlStrategy={urlStrategy} />;
}

📊 Advanced Filtering

Text Filters

  • contains - Partial text matching
  • doesNotContain - Exclusion search
  • startsWith / endsWith - Prefix/suffix matching
  • is / isNot - Exact matching
  • isEmpty / isNotEmpty - Null/empty checks

Number Filters

  • is / isNot - Exact matching
  • isLessThan / isGreaterThan - Comparison operators
  • isBetween / isNotBetween - Range filtering

Date Filters

  • All number operators plus date-specific comparisons
  • isBefore / isAfter - Date comparisons
  • isOnOrBefore / isOnOrAfter - Inclusive date ranges

Select Filters (with faceted counts)

  • isAnyOf / isNoneOf - Multi-select filtering
  • Live counts showing how many items match each option
  • Like Amazon/Etsy filter experience

📊 Data Export

Export table data in multiple formats with advanced customization options:

import { DataTable, DataTableExport } from "@multiplatform.one/table";

function MyTable() {
  const columns = [
    { accessorKey: "name", header: "Name" },
    { accessorKey: "email", header: "Email" },
    { accessorKey: "price", header: "Price", cell: (info) => `$${info.getValue()}` },
  ];

  // Custom export handler (optional)
  const handleExport = async (options) => {
    // Call your API or use libraries like xlsx, jsPDF
    console.log("Exporting:", options.format, options.filename);
  };

  return (
    <DataTable
      data={products}
      columns={columns}
      renderToolbar={(props) => (
        <DataTableToolbar
          {...props}
          customActions={
            <DataTableExport
              data={products}
              columns={columns}
              filename="products-export"
              onExport={handleExport}
            />
          }
        />
      )}
    />
  );
}

Export Features

  • Multiple Formats: CSV, JSON, Excel, PDF, Print
  • Column Selection: Choose which columns to export
  • Custom Filename: Specify export filename
  • Header Control: Include/exclude column headers
  • Server Integration: Custom export handlers for large datasets
  • Rich Content: Exports cell renderer content, not raw data

🔍 Full-Text Search with Highlighting

Powerful search functionality that searches across all columns with real-time highlighting:

import { DataTable, DataTableSearch } from "@multiplatform.one/table";

function MyTable() {
  const [filteredData, setFilteredData] = useState(data);
  const [searchOptions, setSearchOptions] = useState(null);

  // Columns with highlighting
  const columns = [
    {
      accessorKey: "name",
      header: "Name",
      cell: (info) => {
        const value = info.getValue();
        return searchOptions?.highlightResults && searchOptions.searchTerm
          ? highlightTextInCell(value, searchOptions)
          : value;
      },
    },
    // ... other columns with highlighting
  ];

  const handleSearchResults = (results, options) => {
    setFilteredData(results);
    setSearchOptions(options);
  };

  return (
    <DataTable
      data={filteredData}
      columns={columns}
      renderToolbar={(props) => (
        <DataTableToolbar
          {...props}
          customActions={
            <DataTableSearch
              data={data}
              columns={columns}
              onSearchResults={handleSearchResults}
              placeholder="Search across all columns..."
              showAdvancedOptions={true}
            />
          }
        />
      )}
    />
  );
}

Search Features

  • Multi-column search: Search across all text fields simultaneously
  • Real-time highlighting: Search terms highlighted in results
  • Advanced options: Case sensitivity, whole word, regex support
  • Column selection: Choose which columns to search in
  • Debounced search: Optimized performance for large datasets
  • Flexible API: Component-based or hook-based usage

📅 Advanced Date Filtering

Powerful date filtering with presets, ranges, and calendar integration:

import { DataTableDateFilter, type DateFilterValue } from "@multiplatform.one/table";

function MyTable() {
  const [dateFilter, setDateFilter] = useState<DateFilterValue>();

  return (
    <DataTableDateFilter
      value={dateFilter}
      onChange={setDateFilter}
      placeholder="Filter by date..."
      showPresets={true}
      customPresets={[
        {
          id: "lastWeekOrders",
          label: "Ordered Last Week",
          operator: "isLastWeek",
          icon: <CalendarIcon />,
        },
      ]}
    />
  );
}

Date Filter Features

  • Quick Presets: Today, Yesterday, This Week, Last 7/30 Days, etc.
  • Relative Dates: This Month, Last Month, This Year, etc.
  • Custom Ranges: Select start and end dates manually
  • Comparison Operators: Is, Before, After, Between, etc.
  • Empty Handling: Filter for records with/without dates
  • Multiple Filters: Combine different date field filters
  • Calendar Integration: Native HTML5 date inputs

📊 Grouping & Aggregation

Powerful data analysis with hierarchical grouping and calculated aggregations:

import { DataTableGrouping, DataTableGroupingConfig } from "@multiplatform.one/table";

function SalesReport() {
  const [groupingConfig, setGroupingConfig] = useState({
    columns: ["region", "category"], // Multi-level grouping
    aggregations: [
      {
        column: "revenue",
        function: "sum",
        label: "Total Revenue",
      },
      {
        column: "quantity",
        function: "count",
        label: "Order Count",
      },
      {
        column: "revenue",
        function: "avg",
        label: "Average Order",
      },
    ],
  });

  return (
    <DataTableGrouping
      data={salesData}
      columns={columns}
      groupBy={groupingConfig.columns}
      aggregations={groupingConfig.aggregations}
      showAggregations={true}
      renderGroupHeader={(group, level) => (
        <div
          style={{
            backgroundColor: level === 0 ? "#e3f2fd" : "#f3e5f5",
            padding: "12px",
            borderLeft: `4px solid ${level === 0 ? "#2196f3" : "#9c27b0"}`,
          }}
        >
          <h3>
            {group.value} ({group.count} items)
          </h3>
          <div>Total Revenue: ${group.aggregations.revenue?.sum?.toLocaleString()}</div>
        </div>
      )}
    />
  );
}

Grouping Features

  • Multi-level grouping - Group by multiple columns hierarchically
  • Dynamic aggregations - Sum, count, average, min, max calculations
  • Custom aggregation functions - Define your own calculations
  • Expandable/collapsible groups - Show/hide group details
  • Group state persistence - Remember which groups are expanded
  • Performance optimized - Efficient grouping algorithms
  • Custom group headers - Flexible rendering and styling

Aggregation Functions

  • Count - Number of items in group
  • Sum - Total of numeric values
  • Average - Mean of numeric values
  • Min/Max - Smallest/largest values
  • Custom - User-defined aggregation functions

Use Cases

  • Sales Reports - Group by region/product, show totals
  • Financial Data - Group by account type, calculate balances
  • Employee Management - Group by department, show headcount
  • Time Tracking - Group by project, show total hours
  • Product Analytics - Group by category, show performance metrics

Production Setup

For full Excel and PDF support in production:

npm install xlsx jsPDF html2canvas

☑️ Row Selection & Bulk Actions

Complete row selection and bulk operations system:

import { useRowSelection, DataTableBulkActions, createBulkActions } from "@multiplatform.one/table";

function MyTable() {
  // Row selection hook
  const rowSelection = useRowSelection({
    data: users,
    selectionKey: "id",
    onSelectionChange: (selected) => console.log(`${selected.length} items selected`),
  });

  // Define bulk actions
  const bulkActions = createBulkActions([
    {
      id: "send-email",
      label: "Send Email",
      icon: <MailIcon />,
      onExecute: async (selectedItems, clearSelection) => {
        await sendBulkEmail(selectedItems.map((u) => u.email));
        clearSelection();
      },
    },
    {
      id: "export",
      label: "Export CSV",
      icon: <DownloadIcon />,
      onExecute: async (selectedItems) => {
        const csv = convertToCSV(selectedItems);
        downloadCSV(csv, "selected-users.csv");
      },
    },
  ]);

  // Add selection column to table
  const columns = [
    {
      id: "select",
      header: ({ table }) => (
        <Checkbox
          checked={rowSelection.isAllSelected}
          indeterminate={rowSelection.isIndeterminate}
          onCheckedChange={rowSelection.toggleAll}
          aria-label="Select all rows"
        />
      ),
      cell: ({ row }) => (
        <Checkbox
          checked={rowSelection.isSelected(row.original)}
          onCheckedChange={() => rowSelection.toggleItem(row.original)}
          aria-label={`Select ${row.original.name}`}
        />
      ),
      enableSorting: false,
      size: 50,
    },
    // ... other columns
  ];

  return (
    <>
      {/* Bulk actions bar - appears when items are selected */}
      <DataTableBulkActions
        selectedItems={rowSelection.selectedItems}
        onClearSelection={rowSelection.clearSelection}
        actions={bulkActions}
        maxVisibleActions={3}
        showSelectionCount={true}
      />

      <DataTable
        data={users}
        columns={columns}
        enableSorting={true}
        enableFiltering={true}
        enablePagination={true}
      />
    </>
  );
}

Bulk Action Features

  • Confirmation dialogs for destructive actions
  • Loading states during execution
  • Overflow menus for many actions
  • Custom actions with full flexibility
  • Server integration for bulk operations
  • CSV export built-in
  • Selection summary and statistics

Row Selection Features

  • Individual row selection with checkboxes
  • Select all / clear all functionality
  • Indeterminate states for partial selection
  • Keyboard navigation support
  • Accessibility compliant with proper ARIA labels
  • Selection persistence across pagination

🎛️ Forms Integration

Seamlessly integrate with @multiplatform.one/forms for rich filter inputs:

import { DataTable, DataTableFilters } from "@multiplatform.one/table";
import { TextField, SelectField, DatePickerField } from "@multiplatform.one/forms";

function MyTable() {
  return (
    <DataTable
      data={users}
      columns={columns}
      renderFilters={(props) => (
        <DataTableFilters
          {...props}
          availableColumns={[
            {
              id: "name",
              label: "Name",
              type: "text",
              // Custom form component
              renderInput: (props) => (
                <TextField {...props} label="Search by name" placeholder="Enter name..." />
              ),
            },
            {
              id: "department",
              label: "Department",
              type: "select",
              options: [
                { label: "Engineering", value: "eng", count: 25 },
                { label: "Marketing", value: "mkt", count: 12 },
              ],
              // Custom form component
              renderInput: (props) => (
                <SelectField {...props} label="Department" placeholder="Select department..." />
              ),
            },
          ]}
        />
      )}
    />
  );
}

Form Validation Integration

import { useForm } from "@multiplatform.one/forms";

function AdvancedFilters() {
  const form = useForm({
    defaultValues: {
      nameFilter: "",
      salaryRange: { min: "", max: "" },
      joinDate: null,
    },
    onSubmit: ({ value }) => {
      // Convert form values to table filters
      const filters = createTableFiltersFromForm(value);
      tableActions.setFilters(filters);
    },
  });

  return (
    <Form {...form}>
      <TextField name="nameFilter" label="Employee Name" placeholder="Search by name..." />

      <XStack gap="$4">
        <TextField
          name="salaryRange.min"
          label="Min Salary"
          placeholder="50000"
          keyboardType="numeric"
        />
        <TextField
          name="salaryRange.max"
          label="Max Salary"
          placeholder="150000"
          keyboardType="numeric"
        />
      </XStack>

      <DatePickerField name="joinDate" label="Joined After" />

      <SubmitButton>Apply Filters</SubmitButton>
    </Form>
  );
}

📄 Pagination Strategies

Offset-Based Pagination (Traditional)

function OffsetPaginationTable() {
  const [pageData, setPageData] = useState([]);
  const [totalCount, setTotalCount] = useState(0);

  return (
    <DataTable
      data={pageData}
      columns={columns}
      manualPagination={true}
      rowCount={totalCount}
      onStateChange={async (state) => {
        // Server-side pagination
        const response = await api.getUsers({
          page: state.pagination.pageIndex + 1,
          pageSize: state.pagination.pageSize,
          filters: state.filters,
          sorting: state.sorting,
        });

        setPageData(response.data);
        setTotalCount(response.totalCount);
      }}
    />
  );
}

Cursor-Based Pagination (Modern)

function CursorPaginationTable() {
  const [data, setData] = useState([]);
  const [nextCursor, setNextCursor] = useState(null);
  const [previousCursor, setPreviousCursor] = useState(null);

  const loadPage = async (cursor, direction = "forward") => {
    const response = await api.getUsers({
      cursor,
      direction,
      limit: 20,
      filters: activeFilters,
    });

    setData(response.data);
    setNextCursor(response.nextCursor);
    setPreviousCursor(response.previousCursor);
  };

  return (
    <YStack gap="$4">
      <XStack gap="$2">
        <Button disabled={!previousCursor} onPress={() => loadPage(previousCursor, "backward")}>
          Previous
        </Button>

        <Button disabled={!nextCursor} onPress={() => loadPage(nextCursor, "forward")}>
          Next
        </Button>
      </XStack>

      <DataTable
        data={data}
        columns={columns}
        manualPagination={true}
        enablePagination={false} // Disable built-in pagination
      />
    </YStack>
  );
}

Infinite Scroll Pagination

function InfiniteScrollTable() {
  const [data, setData] = useState([]);
  const [hasMore, setHasMore] = useState(true);
  const [isLoading, setIsLoading] = useState(false);

  const loadMore = async () => {
    if (isLoading || !hasMore) return;

    setIsLoading(true);
    const lastItem = data[data.length - 1];

    const response = await api.getUsers({
      cursor: lastItem?.cursor,
      limit: 50,
    });

    setData((prev) => [...prev, ...response.data]);
    setHasMore(response.hasMore);
    setIsLoading(false);
  };

  return (
    <DataTable
      data={data}
      columns={columns}
      manualPagination={true}
      enablePagination={false}
      renderToolbar={(props) => (
        <DataTableToolbar
          {...props}
          customActions={
            hasMore && (
              <Button disabled={isLoading} onPress={loadMore}>
                {isLoading ? "Loading..." : "Load More"}
              </Button>
            )
          }
        />
      )}
    />
  );
}

🔄 Server-Side Operations

Manual Mode Configuration

<DataTable
  data={serverData}
  columns={columns}
  // Manual modes for server-side operations
  manualFiltering={true} // Filters applied server-side
  manualPagination={true} // Pagination handled server-side
  manualSorting={true} // Sorting handled server-side
  // Server callbacks
  onStateChange={(state) => {
    // Send all state to server
    fetchData({
      filters: state.filters,
      pagination: state.pagination,
      sorting: state.sorting,
      globalFilter: state.globalFilter,
    });
  }}
  // Provide metadata for UI
  rowCount={totalServerCount}
  pageCount={totalServerPages}
/>

Real-Time Updates

function RealTimeTable() {
  const [data, setData] = useState([]);
  const [isLoading, setIsLoading] = useState(false);

  useEffect(() => {
    const subscription = api.subscribeToUpdates((update) => {
      // Handle real-time updates
      setData((prev) => updateData(prev, update));
    });

    return () => subscription.unsubscribe();
  }, []);

  const handleFilterChange = async (filters) => {
    setIsLoading(true);
    const filteredData = await api.getFilteredData(filters);
    setData(filteredData);
    setIsLoading(false);
  };

  return (
    <DataTable
      data={data}
      columns={columns}
      onStateChange={(state) => {
        if (state.filters !== lastFilters) {
          handleFilterChange(state.filters);
        }
      }}
      renderToolbar={(props) => (
        <DataTableToolbar {...props} customActions={isLoading && <Text>Updating...</Text>} />
      )}
    />
  );
}
const availableColumns = [
  {
    id: 'role',
    label: 'Role',
    type: 'select',
    options: [
      { label: 'Admin', value: 'admin', count: 5 },
      { label: 'User', value: 'user', count: 23 },
      { label: 'Guest', value: 'guest', count: 12 },
    ],
  },
]

<DataTableFilters
  availableColumns={availableColumns}
  onAddFilter={handleAddFilter}
/>

🔗 URL State Management

// Web: URL updates automatically
// /users?page=2&pageSize=50&filters=name:contains:john&sort=name:asc

// React Native: State persists in memory
// Tauri: State syncs with IPC if needed

<DataTable
  syncWithUrl={true} // Works on all platforms!
  urlDebounceMs={300}
/>

URL State Features

  • 📱 Cross-Platform - Same API everywhere
  • 🔄 Auto-Sync - URL updates as you filter/sort
  • 📋 Shareable - Copy URL to share exact table state
  • 🔙 Browser History - Back/forward buttons work
  • ⚡ Fast - Debounced updates prevent spam

🎛️ View Management

Save and restore filter combinations:

const [views, setViews] = useState([])

<DataTable
  enableViews={true}
  views={views}
  onSaveView={(name) => {
    // Save current filters, sorting, pagination as a view
  }}
  onLoadView={(viewId) => {
    // Load view and apply to table
  }}
/>

🔄 Migration Notes

  • DataTableVirtual remains exported for headless use cases. DataTable with enableVirtualization is the preferred path for full DataTable features (toolbar, filters, pagination) plus virtualization.
  • DataTableColumnPinning remains exported; DataTable integrates it internally when enableColumnPinning is true. Manual composition is still supported.

📊 Server-Side Support

Perfect for large datasets:

function ServerTable({ data, totalCount }) {
  return (
    <DataTable
      data={data} // Current page data
      manualFiltering={true} // We'll handle filtering server-side
      manualPagination={true} // We'll handle pagination server-side
      manualSorting={true} // We'll handle sorting server-side
      rowCount={totalCount} // Total number of rows
      onStateChange={(state) => {
        // Send filters, pagination, sorting to server
        fetchData(state.filters, state.pagination, state.sorting);
      }}
    />
  );
}

🎨 Customization

Custom Toolbar

<DataTable
  renderToolbar={(props) => (
    <DataTableToolbar {...props} customActions={<Button>Export CSV</Button>} />
  )}
/>

Custom Filters

<DataTable
  renderFilters={(props) => <DataTableFilters {...props} maxFilters={3} showFilterCount={true} />}
/>

Custom Pagination

<DataTable
  renderPagination={(props) => (
    <DataTablePagination {...props} showInfo={true} pageSizeOptions={[10, 25, 50, 100]} />
  )}
/>

🔧 API Reference

DataTable Props

| Prop | Type | Default | Description | | -------------------------- | -------------------- | ------- | ------------------------------------------------------------------------------------------- | | data | TData[] | - | Array of data to display | | columns | ColumnDef<TData>[] | - | Column definitions | | syncWithUrl | boolean | false | Enable URL state management | | enableFiltering | boolean | true | Enable advanced filtering | | enableSorting | boolean | true | Enable column sorting | | enablePagination | boolean | true | Enable pagination | | enableGlobalSearch | boolean | true | Enable global search | | enableViews | boolean | false | Enable view management | | manualFiltering | boolean | false | Handle filtering server-side | | manualPagination | boolean | false | Handle pagination server-side | | manualSorting | boolean | false | Handle sorting server-side | | enableVirtualization | boolean | false | Use virtual scrolling for table body. Requires virtualizationHeight when true. | | virtualizationHeight | number | — | Height (px) of scrollable body when enableVirtualization is true. | | enableRowExpansion | boolean | false | Enable expandable sub-rows. Requires getSubRows or subRows in row data. | | enableColumnPinning | boolean | false | Enable column pinning (left/right). Pinned columns stay fixed during horizontal scroll. | | maxPinnedColumns | number | 3 | Max columns per side (left/right) when pinning. | | enableKeyboardNavigation | boolean | false | Enable Excel-like keyboard navigation (arrows, Home, End, PageUp/Down, Enter). | | rowCount | number | — | Total row count for manual pagination (server-side). | | pageCount | number | — | Total page count for manual pagination. If omitted, derived from rowCount and pageSize. |

DataTableFilters Props

| Prop | Type | Default | Description | | ------------------ | ---------------- | ------- | -------------------------------- | | filters | TableFilter[] | [] | Current active filters | | availableColumns | ColumnConfig[] | - | Columns available for filtering | | maxFilters | number | 10 | Maximum number of active filters | | onAddFilter | function | - | Called when filter is added | | onRemoveFilter | function | - | Called when filter is removed |

📦 Bundle Size

Our modular architecture means you only pay for what you use:

// Only imports DataTable core
import { DataTable } from "@multiplatform.one/table";
// ~25KB gzipped

// Includes filtering
import { DataTable, DataTableFilters } from "@multiplatform.one/table";
// ~35KB gzipped

// Everything
import * as Table from "@multiplatform.one/table";
// ~45KB gzipped

🤝 Integration

With @multiplatform.one/forms

Leverage our forms package for rich filter inputs:

import { useFormField } from "@multiplatform.one/forms";
import { DataTableFilters } from "@multiplatform.one/table";

// Forms package provides rich input components
// Table package uses them for advanced filtering

With @multiplatform.one/router

Automatic URL state management:

import { useUrlState } from "@multiplatform.one/router";

// Built-in integration - no extra setup needed
<DataTable syncWithUrl={true} />;

Comparison

What we have ✅

✅ Features We Have:

  • Advanced filtering with complex query builders
  • URL state management and bookmarkable views
  • Column visibility, ordering, and resizing
  • Server-side operations and manual data fetching
  • Row selection and bulk operations
  • Data export (CSV, Excel, PDF, JSON)
  • Full-text search with highlighting
  • Advanced date filtering with calendar picker
  • Accessibility compliance
  • Cross-platform compatibility
  • TypeScript support
  • Views and saved filter combinations
  • Multi-format data export with column selection
  • Bulk actions with confirmation dialogs
  • Advanced filtering across all columns
  • Full-text search with highlighting across all columns
  • Advanced date filtering with calendar picker
  • Server-side pagination and sorting
  • Keyboard navigation
  • Responsive design

❓ Features We Could Add:

  • Virtual scrolling for 100k+ rows
  • Grouping and aggregation
  • Column pinning/freezing
  • Row expansion with details
  • Custom cell renderers
  • Print functionality (enhanced)
  • Undo/redo operations
  • Internationalization
  • Advanced keyboard navigation (Excel-like)

What We Have

  • Native Cross-Platform Support - Works on Web, React Native, Tauri without changes
  • URL State Strategy Pattern - Pluggable routing integration (OneStack, React Router, Next.js)
  • Forms Integration - Deep integration with @multiplatform.one/forms
  • Universal Router Compatibility - Works with any React routing solution

🎯 Current Status: 99% Feature Parity

Implemented:

  • Advanced filtering with complex query builders
  • URL state management and bookmarkable views
  • Column visibility, ordering, and resizing
  • Server-side operations and manual data fetching
  • Row selection and bulk operations
  • Data export (CSV, Excel, PDF, JSON)
  • Full-text search with highlighting
  • Advanced date filtering with calendar picker
  • Virtual scrolling for 100k+ rows 🚀
  • Grouping and aggregation 🚀
  • Accessibility compliance
  • Cross-platform compatibility
  • TypeScript support
  • Views and saved filter combinations

Still Missing (1%):

  • Column pinning/freezing
  • Row expansion with details
  • Custom cell renderers
  • Print functionality (enhanced)
  • Undo/redo operations
  • Internationalization
  • Advanced keyboard navigation (Excel-like)

🧪 Testing

# Run tests
pnpm test

# Run with coverage
pnpm test --coverage

# Run specific test
pnpm test DataTable.test.tsx

📚 Examples

See /examples directory for:

  • AdvancedTableExample - Full-featured table with all bells and whistles
  • GroupingExample - Hierarchical grouping and aggregation
  • VirtualScrollingExample - Handle 100k+ rows with performance
  • BulkActionsExample - Row selection and bulk operations
  • ExportExample - Data export in multiple formats
  • FullTextSearchExample - Advanced search with highlighting
  • DateFilterExample - Advanced date filtering with presets
  • FormsIntegrationExample - Integration with @multiplatform.one/forms
  • SimpleTableExample - Basic table for simple use cases
  • ServerTableExample - Server-side rendering example
  • CustomTableExample - Custom styling and components

🤝 Contributing

We welcome contributions! Please see our Contributing Guide.

📄 License

Apache License 2.0 - see LICENSE file.


Built with ❤️ by BitSpur