Skip to main content
v2.0.3
This guide covers changes to the beta DataTable component. These APIs may continue to evolve before the stable release.

Overview

The DataTable column helper now supports two new properties that improve developer experience:
  • type - Automatically configures display formatting and alignment based on data type
  • editConfig - Consolidates cell editing configuration into a single, explicit opt-in property
These changes reflect our design philosophy of providing sensible defaults while maintaining full customization. Rather than requiring explicit configuration for common patterns, the new API infers reasonable defaults that you can override when needed.

Design Rationale

Why type?

Previously, configuring a currency column required setting multiple independent properties:
// Before: Manual configuration for each concern
createColumn("amount", {
  headerLabel: "Amount",
  align: "end",
  renderCell: (value) => currencyFormatter(value),
});
These properties are conceptually related - a currency column should right-align and use currency formatting. The new type property captures this relationship:
// After: Single property sets sensible defaults
createColumn("amount", {
  headerLabel: "Amount",
  type: "currency", // Sets align: "end" and currencyFormatter automatically
});

Why editConfig?

The previous API scattered edit-related properties across the column definition:
// Before: Edit properties spread across the config
createColumn("status", {
  headerLabel: "Status",
  editMode: "select",
  options: [...],
  onChange: handleChange,
});
This made it unclear which properties were related and whether a column was editable. The new editConfig property makes the intent explicit:
// After: All edit concerns grouped together
createColumn("status", {
  headerLabel: "Status",
  editConfig: {
    mode: "select",
    options: [...],
    onChange: handleChange,
  },
});
Importantly, editing is now opt-in. Without editConfig, columns are read-only regardless of type.

Extensibility

This structure supports future enhancements without breaking changes:
  • New column types (e.g., link, avatar, progress) can be added with appropriate defaults
  • New edit modes (e.g., number with min/max/step, date picker) can include mode-specific options
  • Edit-specific options (e.g., decimalDigits for number mode) live inside editConfig

Column Types Reference

Each type sets default values for align and renderCell:
TypeDefault AlignDefault Formatter
textstartNone (raw value)
numberendnumberFormatter
currencyendcurrencyFormatter
percentendpercentFormatter
dateenddateFormatter
dateTimeenddateTimeFormatter
timeendtimeFormatter
booleanstartbooleanFormatter

Using Type with Custom Options

For simple cases, pass the type as a string:
createColumn("amount", {
  headerLabel: "Amount",
  type: "currency",
});
To customize formatter options, pass an object:
createColumn("amount", {
  headerLabel: "Amount",
  type: { 
    type: "currency", 
    options: { currency: "EUR", locale: "de-DE" } 
  },
});

Overriding Type Defaults

Explicit properties always override type defaults:
createColumn("amount", {
  headerLabel: "Amount",
  type: "currency",
  align: "start", // Overrides the default "end"
  renderCell: (value) => `$${value.toFixed(0)}`, // Overrides currencyFormatter
});

EditConfig Reference

The editConfig property accepts an object with mode, onChange, and mode-specific options.

Text Mode

createColumn("name", {
  headerLabel: "Name",
  editConfig: {
    mode: "text",
    onChange: (value, rowId) => updateName(value, rowId),
  },
});

Select Mode

createColumn("status", {
  headerLabel: "Status",
  editConfig: {
    mode: "select",
    options: [
      { value: "pending", label: "Pending" },
      { value: "active", label: "Active" },
      { value: "completed", label: "Completed" },
    ],
    onChange: (value, rowId) => updateStatus(value, rowId),
  },
});

Multiselect Mode

createColumn("tags", {
  headerLabel: "Tags",
  editConfig: {
    mode: "multiselect",
    options: [
      { value: "urgent", label: "Urgent" },
      { value: "reviewed", label: "Reviewed" },
    ],
    onChange: (value, rowId) => updateTags(value, rowId),
  },
});

Migration Guide

Display-Only Columns

No changes required. Optionally add type for automatic formatting:
// Before
createColumn("amount", {
  headerLabel: "Amount",
  renderCell: (value) => currencyFormatter(value),
});

// After (optional improvement)
createColumn("amount", {
  headerLabel: "Amount",
  type: "currency",
});

Editable Columns

Move editMode, onChange, and options into editConfig:
// Before
createColumn("name", {
  headerLabel: "Name",
  editMode: "text",
  onChange: handleChange,
});

// After
createColumn("name", {
  headerLabel: "Name",
  editConfig: {
    mode: "text",
    onChange: handleChange,
  },
});
For select/multiselect columns:
// Before
createColumn("status", {
  headerLabel: "Status",
  editMode: "select",
  options: statusOptions,
  onChange: handleChange,
});

// After
createColumn("status", {
  headerLabel: "Status",
  editConfig: {
    mode: "select",
    options: statusOptions,
    onChange: handleChange,
  },
});

Deprecation Timeline

The following props show deprecation warnings in development:
  • editMode - Use editConfig.mode instead
  • onChange (top-level) - Use editConfig.onChange instead
  • options (top-level) - Use editConfig.options instead
Existing code continues to work but will log console warnings to guide migration. TypeScript also marks these props with @deprecated JSDoc comments.

New Exports

The following types are now exported from @servicetitan/anvil2:
  • ColumnType - Union of column type literals
  • ColumnTypeConfig - Type configuration (string or object with options)
  • EditConfig, TextEditConfig, SelectEditConfig, MultiselectEditConfig - Edit configuration types
  • getColumnTypeDefaults(), resolveColumnTypeConfig() - Utility functions for working with column types
Last modified on February 11, 2026