Skip to content

Repository files navigation

TableCrafter.js

The zero-dependency JavaScript data table that turns any array, API, or CSV into an editable, filterable, mobile-ready table.

npm versionLicense: MIT~27 KB min+gz

importTableCrafterfrom'tablecrafter';consttable=newTableCrafter('#my-table',{data: '/api/employees',columns: [{field: 'name',label: 'Name',editable: true}],editable: true,});table.render();

Why TableCrafter.js

  • No build step required. Drop the UMD script on any page and you are done. No bundler, no framework, no fuss.
  • Spreadsheet-grade editing in the browser. 14 built-in cell editors, 15+ validation rules, lookup dropdowns, formula columns, and a custom cell-type registry -- all without a server round-trip per keypress.
  • Mobile cards, not squished columns. The responsive card layout collapses rows into readable cards at configurable breakpoints instead of making users scroll sideways.
  • Your data stays yours. No SaaS, no telemetry, no external requests unless you point it at your own API. MIT licensed.

Feature grid

Display

FeatureDetails
Multi-column sortUnlimited sort keys, shift-click, badges, custom comparators
Per-column filtersText, multiselect, date range, number range; type auto-detection
Advanced search grammarAND / OR / negation / field:value / regex / comparison operators
Mobile card viewResponsive breakpoints, expandable sections, field-level visibility
Virtual scrollWindowed rendering via enableVirtualScroll() -- no pagination required
Formula columnsArithmetic, comparisons, IF, CONCAT, LENGTH, UPPER, LOWER
Conditional formattingData bars, color scales, icon sets; ARIA labels on visual-only cues
Heatmap cellsInline SVG heatmap from an array-of-numbers (cellType: 'heatmap')
Cell renderersBadge, link, progress bar, sparkline built-in
Cell range selectionClick-drag range with TSV clipboard copy
Right-click context menuARIA-compliant, fully configurable items; keyboard navigation
Column managementProgrammatic reorder, show/hide, pin left/right
RTL supportLocale-driven layout flip (dir="rtl" + tc-rtl class)
i18n6 bundled locales: en, es, fr, de, ar, ur; custom number/date formatters

Editing

FeatureDetails
14 inline cell editorstext, textarea, number, email, date, datetime, select, multiselect, checkbox, radio, file, url, color, range
Custom cell type registryregisterCellType() for any editor
15+ validation rulesrequired, minLength, maxLength, min, max, pattern, email, url, phone, date bounds, oneOf, notOneOf, unique, custom function
Add row modalBuilt-in creation form with full validation
Bulk operationsMulti-row select, delete, export, custom actions
Role-based permissionsPer-action (view/edit/delete/create), row-level ownOnly ownership

Data

FeatureDetails
Inline dataPlain JavaScript array
REST APIFetch from any URL; custom auth headers; root path; CRUD write-back
CSV exportRFC-4180, filtered, injection-safe
JSON exportSerialized current dataset
XLSX / PDF exportRequires optional peer deps -- see Installation
State persistencelocalStorage / sessionStorage; saves filters, sort, page
Plugin systemuse(plugin, opts) / unuse(name) with full lifecycle hooks
Events APIon / off / once for 8 named events

Credential-bearing sources (Airtable, Notion, SQL databases) must not be reached directly from the browser — their tokens and connection strings would be exposed. Put a thin server proxy in front of them: see docs/server-proxy-recipes.md for Airtable, Notion, and SQL recipes.

Platform

FeatureDetails
Zero runtime dependenciesPure vanilla JavaScript
ESM + CJS + UMD buildsWorks with webpack, Vite, Rollup, or a plain <script> tag
CDN auto-initTableCrafter.bootstrap() from data-tc-bootstrap attributes
TypeScript definitionsFull .d.ts included (src/tablecrafter.d.ts)
Framework-agnosticReact, Vue, Svelte, Angular, or plain HTML
jsDelivrhttps://cdn.jsdelivr.net/npm/tablecrafter@2/dist/tablecrafter.umd.min.js

Installation

CDN (no build step)

<linkrel="stylesheet" href="https://cdn.jsdelivr.net/npm/tablecrafter@2/dist/tablecrafter.css"><scriptsrc="https://cdn.jsdelivr.net/npm/tablecrafter@2/dist/tablecrafter.umd.min.js"></script>

The global is TableCrafter.

npm

npm install tablecrafter
importTableCrafterfrom'tablecrafter';import'tablecrafter/dist/tablecrafter.css';

Optional peer dependencies (XLSX / PDF export)

npm install xlsx # XLSX export
npm install jspdf jspdf-autotable # PDF export

Quick start

<divid="my-table"></div><scriptsrc="https://cdn.jsdelivr.net/npm/tablecrafter@2/dist/tablecrafter.umd.min.js"></script><script>consttable=newTableCrafter('#my-table',{data: [{id: 1,name: 'Alice',role: 'Engineer'},{id: 2,name: 'Bob',role: 'Designer'},],columns: [{field: 'id',label: 'ID'},{field: 'name',label: 'Name',editable: true},{field: 'role',label: 'Role',editable: true},],editable: true,filterable: true,pagination: true,});table.render();</script>

CDN auto-init (no JavaScript required)

Any element with a data-tc-bootstrap attribute is instantiated automatically when TableCrafter.bootstrap() runs. The configuration comes from the element's data-tc-config JSON attribute:

<divdata-tc-bootstrapdata-tc-config='{"columns":[{"field":"name","label":"Name"}], "data":[{"name":"Alice"},{"name":"Bob"}], "filterable":true}'></div><scriptsrc="https://cdn.jsdelivr.net/npm/tablecrafter@2/dist/tablecrafter.umd.min.js"></script><script>consttables=TableCrafter.bootstrap();// Map<HTMLElement, TableCrafter>// TableCrafter.bootstrap('#my-section'); // scope to a subtree</script>

Live examples

The examples/ directory contains runnable HTML files. Start any static server from the repo root:

python -m http.server 8000
# then open http://localhost:8000/examples/advanced-features.html

WordPress?

TableCrafter.js shares feature DNA with the TableCrafter WordPress plugin. If you need server-side data sources, Gravity Forms write-back, Gutenberg blocks, or WooCommerce integration, the plugin handles those. For the current gap status between the two, see docs/PARITY.md.

Visit tablecrafter.com for the plugin.


Configuration reference

Constructor: new TableCrafter(container, config) where container is a CSS selector string or HTMLElement.

Core options

KeyTypeDefaultDescription
dataarray | string[]Row data or URL to fetch
columnsarray[]Column definitions (required)
editablebooleanfalseEnable inline editing globally
sortablebooleantrueEnable column sorting
filterablebooleantrueEnable per-column filters
globalSearchbooleantrueEnable the search grammar bar
paginationbooleanfalseEnable pagination
pageSizenumber25Rows per page
exportablebooleanfalseShow CSV export button
exportFilenamestring'table-export.csv'Default download filename

Column definition

{field: 'email',// data key (required)label: 'Email',// header texttype: 'email',// cell renderer: text|badge|link|progress|sparkline|...editable: true,// enable this column for editingsortable: true,filterable: true,formula: 'price * qty',// computed column expressionaggregate: 'sum',// sum | count | avg | min | max | distinctcellType: 'heatmap',// built-in cell type overridelookup: {url: '/api/users',valueField: 'id',labelField: 'name',},}

Editing and validation example

consttable=newTableCrafter('#table',{data: rows,columns: [{field: 'email',label: 'Email',editable: true},{field: 'age',label: 'Age',editable: true},],validation: {rules: {email: [{type: 'required'},{type: 'email'}],age: [{type: 'min',value: 18},{type: 'max',value: 120}],},},onEdit({ row, field, value }){console.log(`${field} changed to`,value);},});table.render();

Permissions example

consttable=newTableCrafter('#table',{data: rows,
columns,permissions: {enabled: true,edit: ['admin','manager'],delete: ['admin'],ownOnly: true,// users see only rows they own},});table.setCurrentUser({id: 42,roles: ['manager'],username: 'alice'});table.render();

Per-column edit restrictions

Restrict an individual column's inline editor to specific roles with editableRoles, and (optionally) show a tooltip on cells the current user cannot edit:

consttable=newTableCrafter('#table',{data: rows,columns: [{field: 'name',label: 'Name',editable: true},{field: 'salary',label: 'Salary',editable: true,editableRoles: ['admin']},],roles: ['viewer'],// or call table.setCurrentUser({ roles: [...] })showPermissionTooltip: true,// tooltip on restricted cells});

Advisory only.editableRoles, permissions, and every other role check in TableCrafter.js are client-side hints. They hide UI and block the editor in the browser, but a determined user can bypass them. Your server must enforce the same restrictions independently as the source of truth.


i18n

consttable=newTableCrafter('#table',{data: rows,
columns,i18n: {locale: 'ar',// Arabic -- triggers RTL layout automaticallyfallbackLocale: 'en',messages: {ar: {'search.placeholder': 'ابحث...'},},},});

Six locales are bundled: en, es, fr, de, ar, ur. RTL is applied automatically for ar and ur.

Custom number and date formatters

Supply i18n.formats to override how number and date columns render:

i18n: {locale: 'de',formats: {// Intl.NumberFormat options object, or a function(value, locale) => stringformatNumber: {style: 'currency',currency: 'EUR'},// Function(value, locale) => stringformatDate: (value,locale)=>newIntl.DateTimeFormat(locale,{dateStyle: 'medium'}).format(newDate(value)),},},

Runtime locale switching

table.setLocale('fr');// Register or override message keys for any localetable.addMessages('es',{'toolbar.search': 'Buscar...'});

Plugin system

constmyPlugin={name: 'my-plugin',install(table,opts){// extend or monkey-patch `table` hereconsole.log('installed with',opts);},};consttable=newTableCrafter('#table',{data: rows,
columns,plugins: [[myPlugin,{debug: true}]],});

Lifecycle hooks

Plugins declare hooks inside a hooks object. Return false from any before* hook to cancel the operation. Available pairs: beforeRender/afterRender, beforeSort/afterSort, beforeEdit/afterEdit (payloads: { rowIndex, field, value } / { rowIndex, field, oldValue, newValue }), beforeLoad/afterLoad (payloads: { source } / { data }), and destroy.

constguardPlugin={name: 'guard',hooks: {beforeEdit: ({ field, value })=>{if(field==='salary'&&value<0)returnfalse;// cancel},afterEdit: ({ field, oldValue, newValue })=>{console.log(`${field}: ${oldValue} -> ${newValue}`);},destroy: ()=>console.log('teardown'),},};

Events

The events API lets you observe table activity from outside the config callbacks. on() and once() both return an unsubscribe function.

// Persistent subscriptionconstunsub=table.on('cellEdit',({ row, field, oldValue, newValue })=>{console.log(`Row ${row}: ${field} changed from ${oldValue} to ${newValue}`);});// One-shot subscription (auto-removes after the first firing)table.once('rowAdd',({ row, index })=>{console.log('First row added at index',index,':',row);});// Unsubscribe via the returned functionunsub();// Or by referenceconsthandler=({ page })=>console.log('page',page);table.on('pageChange',handler);table.off('pageChange',handler);

Event reference

EventPayloadFired by
cellEdit{ row, field, oldValue, newValue }saveEdit() / cell commit
selectionChange{ selectedRows }toggleRowSelection()
sort{ sortKeys }sort() / multiSort()
filter{ filters }setFilter() / clearFilters()
pageChange{ page }goToPage() / nextPage() / prevPage()
rowAdd{ row, index }addRow()
rowUpdate{ row, index, previous }updateRow()
rowDelete{ row, index }removeRow()

Config callbacks (onEdit, onSort, etc.) still fire alongside events. A throwing handler is caught and logged; other handlers in the same event still run. See examples/events-and-hooks.html.


TypeScript

The package ships src/tablecrafter.d.ts. The types field in package.json points to it automatically.

importTableCrafter,{TableCrafterConfig,TableCrafterColumn}from'tablecrafter';constcolumns: TableCrafterColumn[]=[{field: 'id',label: 'ID'},{field: 'name',label: 'Name',editable: true},];consttable=newTableCrafter('#table',{data: [], columns });table.render();

Framework integration

React

import{useEffect,useRef}from'react';importTableCrafterfrom'tablecrafter';exportfunctionDataTable({ data, onEdit }){constref=useRef(null);useEffect(()=>{consttable=newTableCrafter(ref.current,{ data, onEdit,columns: []});table.render();return()=>table.destroy();},[data]);return<divref={ref}/>;}

Export

CSV and JSON export work out of the box:

table.downloadCSV();// triggers browser downloadconstjson=table.exportToJSON();

XLSX and PDF export require optional peer dependencies (install commands above under Installation).


API methods (summary)

MethodDescription
render()Render or re-render the table
destroy()Tear down and remove listeners
setData(rows)Replace all data
getData()Current (unfiltered) data
getFilteredData()Data after search/filter applied
addRow(row)Append a row
updateRow(i, updates)Patch a row by index
removeRow(i)Delete a row by index
setFilter(field, value)Set a column filter
clearFilters()Reset all filters
sort(field, opts?)Sort by field
multiSort(keys)Sort by multiple fields
goToPage(n)Jump to page
exportToCSV()Return CSV string
exportToJSON()Return JSON string
downloadCSV()Trigger browser CSV download
setCurrentUser(user)Set user context for permissions
hasPermission(action, row?)Check permission
use(plugin, opts?)Register a plugin
unuse(name)Remove a plugin by name
setLocale(locale)Switch i18n locale at runtime
addMessages(locale, messages)Register or override i18n strings
on(event, handler)Subscribe to a named event; returns unsub function
off(event, handler)Unsubscribe a handler
once(event, handler)Subscribe for one emission; returns unsub function
enableVirtualScroll(opts?)Enable windowed rendering (rowHeight, viewportHeight, overscan)
disableVirtualScroll()Disable virtual scroll
isVirtualScrolling()Returns true when virtual scroll is active
getAggregates()Aggregated column values (sum / count / avg / min / max / distinct)
saveState()Persist current state
loadState()Restore persisted state
snapshotHTML(opts?)Deterministic HTML snapshot for testing (`scope: 'table'
TableCrafter.bootstrap(scope?)Auto-init from [data-tc-bootstrap] elements; returns Map
TableCrafter.getBrowserSupport()Capability probe returning { intl, resizeObserver, requiredFeaturesAvailable, ... }
TableCrafter.minimumBrowserSupportNotice()Human-readable string listing minimum requirements

Testing

npm test# run Jest suite
npm run test:watch
npm run test:coverage

Contributing

  1. Fork the repository
  2. Create a feature branch: git checkout -b feature/description
  3. Add tests for new functionality
  4. Ensure all tests pass: npm test
  5. Open a pull request against main

Roadmap: parity gaps and planned work are tracked in Epic #323 and the parity matrix.


License

MIT -- see LICENSE.

About

Zero-dependency JavaScript data table: inline editing, advanced filters, formula columns, mobile card view. Companion to the TableCrafter WordPress plugin.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages