Feature-Rich Data Grid Built on TanStack Table – Svelte TableCN

Build data tables in Svelte 5 with TableCN. Supports cell editing, selection, copy/paste, search, and row virtualization.

Svelte TableCN is a data table & data grid component that allows you to handle complex table interactions in Svelte applications. The library ports the original TableCN implementation to work with Svelte’s latest version and runes system.

The component supports multiple cell types, including text, numbers, dates, selects, checkboxes, URLs, and file uploads. Each cell type includes specific validation and formatting options that match real data entry patterns.

Features

📝 Multiple Cell Types: Text, number, date, select, multi-select, checkbox, URL, and file cells handle different data formats with built-in validation.

âŒ¨ī¸ Keyboard Navigation: Arrow keys, Tab, Enter, and Escape let users move through cells and edit data without reaching for the mouse.

đŸŽ¯ Cell Selection: Single-cell selection, multi-select with Ctrl/Cmd, and range selection with Shift support different selection workflows.

📋 Copy and Paste: Copy single cells or ranges and paste data back into the grid with standard keyboard shortcuts.

🔍 Search with Highlighting: Find specific values across all columns and see matches highlighted in the interface.

📊 Column Operations: Sort, filter, pin, resize, and toggle column visibility to customize the view for different tasks.

⚡ Row Virtualization: Render only visible rows to maintain smooth scrolling and interaction with large datasets.

đŸ–ąī¸ Context Menu: Right-click cells to access common operations like cut, copy, paste, and delete.

â†Šī¸ Undo and Redo: Track changes and revert edits with standard Ctrl/Cmd + Z keyboard shortcuts.

📏 TypeScript: Includes full type definitions.

đŸ—ī¸ TanStack Integration: Uses TanStack Table for state management and TanStack Virtual for efficient rendering.

Use Cases

  • Admin Dashboards: Manage user lists, product inventories, or transaction logs where inline editing is required.
  • Data Management Interfaces: Let users review and update spreadsheet-like information within a web application.
  • Internal Tools: Build custom CRM, project management, or analytics panels for team use.

How to Use It

1. To get started, make sure your project has shadcn-svelte configured with Tailwind CSS.

bunx shadcn-svelte@latest init

2. Add Svelte TableCN to your project using the shadcn-svelte CLI.

bunx shadcn-svelte@latest add https://svelte-tablecn.vercel.app/r/data-grid.json

3. Import the DataGrid component and useDataGrid hook in your Svelte file. Define your data type with TypeScript to get proper type checking for column definitions and cell values.

<script lang="ts">
  import { DataGrid } from '$lib/components/data-grid';
  import { useDataGrid } from '$lib/hooks/use-data-grid.svelte';
  import type { ColumnDef } from '@tanstack/table-core';
  type Product = {
    id: string;
    name: string;
    price: number;
    category: string;
    inStock: boolean;
    releaseDate: string;
  };
  const categories = ['Electronics', 'Clothing', 'Books', 'Home'];
  let products = $state<Product[]>([
    {
      id: '1',
      name: 'Laptop',
      price: 999,
      category: 'Electronics',
      inStock: true,
      releaseDate: '2024-01-15'
    },
    {
      id: '2',
      name: 'T-Shirt',
      price: 29,
      category: 'Clothing',
      inStock: false,
      releaseDate: '2024-02-20'
    },
    {
      id: '3',
      name: 'Novel',
      price: 15,
      category: 'Books',
      inStock: true,
      releaseDate: '2024-03-10'
    }
  ]);
</script>

4. Configure your columns using TanStack Table column definitions. Each column needs an accessor key that matches your data properties and a meta object that defines the cell variant and options.

<script lang="ts">
  const columns: ColumnDef<Product, unknown>[] = [
    {
      accessorKey: 'name',
      header: 'Product Name',
      meta: { cell: { variant: 'short-text' } }
    },
    {
      accessorKey: 'price',
      header: 'Price',
      meta: {
        cell: {
          variant: 'number',
          min: 0,
          step: 0.01
        }
      }
    },
    {
      accessorKey: 'category',
      header: 'Category',
      meta: {
        cell: {
          variant: 'select',
          options: categories.map(c => ({ label: c, value: c }))
        }
      }
    },
    {
      accessorKey: 'releaseDate',
      header: 'Release Date',
      meta: { cell: { variant: 'date' } }
    },
    {
      accessorKey: 'inStock',
      header: 'In Stock',
      meta: { cell: { variant: 'checkbox' } }
    }
  ];
</script>

5. Initialize the data grid with useDataGrid and pass your data, columns, and event handlers. The hook returns table state and props that you spread onto the DataGrid component.

<script lang="ts">
  const { table, ...dataGridProps } = useDataGrid({
    data: () => products,
    columns,
    onDataChange: (updatedProducts) => {
      products = updatedProducts;
    },
    getRowId: (row) => row.id,
    enableSearch: true,
    enablePaste: true
  });
</script>
<DataGrid {...dataGridProps} {table} height={500} />

6. Add row operations by implementing the onRowAdd and onRowsDelete callbacks. These handlers receive the current state and let you update your data array with new or removed rows.

<script lang="ts">
  const { table, ...dataGridProps } = useDataGrid({
    data: () => products,
    columns,
    onDataChange: (updatedProducts) => {
      products = updatedProducts;
    },
    onRowAdd: () => {
      const newProduct: Product = {
        id: crypto.randomUUID(),
        name: '',
        price: 0,
        category: categories[0],
        inStock: false,
        releaseDate: new Date().toISOString().split('T')[0]
      };
      products = [...products, newProduct];
    },
    onRowsDelete: (rows, indices) => {
      products = products.filter((_, index) => !indices.includes(index));
    },
    getRowId: (row) => row.id,
    enableSearch: true
  });
</script>

7. Handle file uploads by implementing the onFilesUpload callback. The function receives file objects and metadata, processes the uploads, and returns file cell data with URLs and display information.

<script lang="ts">
  const handleFileUpload = async (params: {
    files: File[];
    rowIndex: number;
    columnId: string;
  }) => {
    // Upload files to your storage service
    const uploadedFiles = await Promise.all(
      params.files.map(async (file) => {
        const formData = new FormData();
        formData.append('file', file);
        const response = await fetch('/api/upload', {
          method: 'POST',
          body: formData
        });
        const { url } = await response.json();
        return {
          id: crypto.randomUUID(),
          name: file.name,
          url: url,
          size: file.size,
          type: file.type
        };
      })
    );
    return uploadedFiles;
  };
  const { table, ...dataGridProps } = useDataGrid({
    data: () => products,
    columns,
    onFilesUpload: handleFileUpload,
    onFilesDelete: (params) => {
      console.log('Deleting files:', params.files);
    },
    getRowId: (row) => row.id
  });
</script>

API Reference

useDataGrid Configuration

OptionTypeDescription
dataTData[] | (() => TData[])Array of row data or getter function that returns data for reactive updates.
columnsColumnDef<TData>[]Column definitions following TanStack Table format with cell variant metadata.
getRowId(row: TData) => stringFunction that returns unique identifier for each row.
enableSearchbooleanActivates search bar and highlighting across all columns.
enablePastebooleanAllows pasting data from clipboard into cells and ranges.
readOnlybooleanDisables all editing interactions when set to true.
rowHeight'short' | 'medium' | 'tall' | 'extra-tall'Sets row height preset for the entire grid.
initialStateobjectInitial state for sorting, filtering, column visibility, and pagination.
onDataChange(data: TData[]) => voidCalled with updated data array when any cell value changes.
onRowAdd() => voidTriggered when user adds a new row through the interface.
onRowsAdd(count: number) => voidCalled when multiple rows are added at once with row count.
onRowsDelete(rows: TData[], indices: number[]) => voidReceives deleted rows and their indices when rows are removed.
onFilesUpload(params: FileUploadParams) => Promise<FileCellData[]>Handles file upload logic and returns file metadata with URLs.
onFilesDelete(params: FileDeleteParams) => voidCalled when files are removed from file cells.

Cell Variant Options

VariantDescriptionConfiguration
short-textSingle line text input for brief content.No additional options required.
long-textMulti-line text area with expandable editor for longer content.No additional options required.
numberNumeric input with increment/decrement controls.min, max, step for validation and stepping.
dateDate picker with calendar interface.No additional options required.
selectDropdown with single selection from predefined options.options array with label and value pairs.
multi-selectMultiple selection with tag display for chosen items.options array with label and value pairs.
checkboxBoolean toggle for true/false values.No additional options required.
urlURL input with validation and link preview.No additional options required.
fileFile upload cell with drag and drop support.maxFiles, maxFileSize, accept for upload constraints.
row-selectCheckbox in first column for row selection.No additional options required.

Column Meta Configuration

meta: {
  label: string;              // Display label for column header
  cell: {
    variant: CellVariant;     // Type of cell editor and renderer
    min?: number;             // Minimum value for number cells
    max?: number;             // Maximum value for number cells
    step?: number;            // Increment step for number cells
    options?: Array<{         // Options for select and multi-select
      label: string;
      value: string;
    }>;
    maxFiles?: number;        // Maximum files for file cells
    maxFileSize?: number;     // Maximum file size in bytes
    accept?: string;          // Accepted file MIME types
  }
}

Keyboard Shortcuts

ShortcutAction
Arrow KeysNavigate between cells in all directions.
TabMove to next cell, wrapping to next row at end.
Shift + TabMove to previous cell, wrapping to previous row at start.
EnterStart editing current cell or move down when editing.
EscapeCancel editing mode or clear current selection.
Ctrl/Cmd + CCopy selected cells to clipboard.
Ctrl/Cmd + VPaste clipboard content into selected cells.
Ctrl/Cmd + XCut selected cells to clipboard.
Ctrl/Cmd + ZUndo last change to data.
Ctrl/Cmd + Shift + ZRedo previously undone change.
Ctrl/Cmd + FOpen search dialog.
DeleteClear content from selected cells.
BackspaceClear content and start editing.

Related Resources

  • TanStack Table: Headless table library that handles sorting, filtering, pagination, and column operations.
  • TanStack Virtual: Virtualization library that renders only visible rows for performance with large datasets.
  • shadcn-svelte: Component collection built on Bits UI with Tailwind CSS styling for Svelte applications.
  • Bits UI: Headless component library for Svelte that handles accessibility and keyboard interactions.

FAQs

Q: Can I use this component with Svelte 4?
A: No. Svelte TableCN requires Svelte 5+ because it uses the new runes system for reactivity.

Q: How do I customize the appearance of cells?
A: The component uses Tailwind CSS classes through shadcn-svelte. You can modify the component files in your project after installation to adjust colors, spacing, and typography to match your design system.

Q: Does the grid support server-side operations?
A: The component handles all operations client-side by default. You need to implement server-side logic in the onDataChange, onRowAdd, and onRowsDelete callbacks to sync changes with your backend API.

itisyb

itisyb

Leave a Reply

Your email address will not be published. Required fields are marked *