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

Overview

This guide continues the work started in Column Types and EditConfig, which introduced the editConfig property and the text, select, and multiselect edit modes. That guide also outlined how the editConfig structure supports new edit modes with mode-specific options — the number edit mode is the first to use this pattern. The DataTable now supports a dedicated number edit mode for inline editing of numeric cell values:
  • editConfig.mode: "number" - Renders a formatted number input with validation, decimal precision, and keyboard increment/decrement
  • Number-specific options - Configure minValue, maxValue, step, and fraction digit precision directly on editConfig
Previously, editable text cells silently coerced numeric values to strings. The new number edit mode handles numeric data natively, preserving type safety and providing a better editing experience.

Design Rationale

Why a dedicated number mode?

The text edit mode previously accepted number values by converting them to strings:
// Before: Number values silently coerced to strings
createColumn("amount", {
  headerLabel: "Amount",
  editConfig: {
    mode: "text",
    onChange: (value, rowId) => {
      // value is a string — requires manual parsing
      updateAmount(parseFloat(value), rowId);
    },
  },
});
This approach had several problems:
  • Values lost their numeric type, requiring manual parsing in onChange
  • No input validation for numeric constraints like min/max
  • No keyboard increment/decrement support
  • No decimal precision formatting
The new number mode addresses all of these:
// After: Purpose-built number editing
createColumn("amount", {
  headerLabel: "Amount",
  type: "currency",
  editConfig: {
    mode: "number",
    minValue: 0,
    maxValue: 100000,
    step: 0.01,
    maximumFractionDigits: 2,
    onChange: (value, rowId) => {
      // value is number | null — no parsing needed
      updateAmount(value, rowId);
    },
  },
});

Relationship between type and editConfig

The type property controls how a value is displayed when the cell is not being edited. The editConfig controls the editing behavior. These are independent concerns:
// Display as currency, edit as number
createColumn("amount", {
  headerLabel: "Amount",
  type: "currency",         // Formats display as "$1,234.56"
  editConfig: {
    mode: "number",         // Edits with a number input
    minimumFractionDigits: 2,
    onChange: handleChange,
  },
});

Number Edit Options Reference

Number-specific options are set directly on the editConfig object alongside mode and onChange. All options are optional.
OptionTypeDescription
minValuenumberMinimum allowed value
maxValuenumberMaximum allowed value
stepnumberIncrement/decrement step for arrow key navigation
minimumFractionDigitsnumberMinimum decimal places displayed
maximumFractionDigitsnumberMaximum decimal places allowed
These options match the useNumberField hook configuration.

Basic number editing

createColumn("quantity", {
  headerLabel: "Quantity",
  type: "number",
  editConfig: {
    mode: "number",
    minValue: 0,
    step: 1,
    onChange: (value, rowId) => updateQuantity(value, rowId),
  },
});

Currency editing with decimal precision

createColumn("amount", {
  headerLabel: "Amount",
  type: "currency",
  editConfig: {
    mode: "number",
    minValue: 0,
    maxValue: 100000,
    step: 0.01,
    maximumFractionDigits: 2,
    minimumFractionDigits: 2,
    onChange: (value, rowId) => updateAmount(value, rowId),
  },
});

Migration Guide

Numeric columns using text edit mode

If you were using editMode: "text" or editConfig.mode: "text" for numeric data, switch to number mode:
// Before: Text mode with manual number handling
createColumn("amount", {
  headerLabel: "Amount",
  editMode: "text",
  onChange: (value, rowId) => {
    const parsed = parseFloat(value);
    if (!isNaN(parsed)) updateAmount(parsed, rowId);
  },
});

// After: Number mode with native number handling
createColumn("amount", {
  headerLabel: "Amount",
  type: "currency",
  editConfig: {
    mode: "number",
    minValue: 0,
    maximumFractionDigits: 2,
    onChange: (value, rowId) => updateAmount(value ?? 0, rowId),
  },
});

Numeric columns using editConfig text mode

// Before: editConfig text mode for numbers
createColumn("price", {
  headerLabel: "Price",
  editConfig: {
    mode: "text",
    onChange: (value, rowId) => updatePrice(Number(value), rowId),
  },
});

// After: editConfig number mode
createColumn("price", {
  headerLabel: "Price",
  type: "currency",
  editConfig: {
    mode: "number",
    step: 0.01,
    minimumFractionDigits: 2,
    maximumFractionDigits: 2,
    onChange: (value, rowId) => updatePrice(value ?? 0, rowId),
  },
});

Breaking Changes

The text edit mode no longer coerces numeric values to strings. Columns with numeric data using text edit mode will render as read-only and log a warning.
The following change is breaking:
  • Text edit mode no longer accepts number values - Previously, a cell with mode: "text" and a numeric value would silently convert the number to a string. Now, the cell falls back to a read-only state and logs a console warning directing you to use mode: "number" instead.

Why breaking?

Since DataTable is a beta component, we opted to remove the implicit coercion rather than maintain a deprecation path. The coercion masked type mismatches and produced a suboptimal editing experience for numeric data. The new number edit mode is the correct replacement.

New Exports

The following types are now exported from @servicetitan/anvil2:
  • NumberEditConfig - Edit configuration type for mode: "number" columns
Last modified on February 18, 2026