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

Overview

The legacy top-level props for configuring editable cells have been removed from ColumnDef<T>:
  • editMode — Removed. Use editConfig.mode instead.
  • onChange — Removed. Use editConfig.onChange instead.
  • options — Removed. Use editConfig.options instead.
These props were deprecated in favor of editConfig and have now been fully removed. All editable cell configuration is done through the editConfig object.

Design Rationale

Why remove instead of deprecate further?

The recent switch to SelectMenuSync and MultiSelectMenuSync for select and multiselect edit cells changed the options shape and onChange signatures. Maintaining backward compatibility with the legacy top-level props for these new internals was cumbersome — each editable cell component needed adapters to translate between the old { value, label } format and the new { id, label } format, and to convert onChange callbacks back and forth. Since DataTable is a beta component, removing the legacy API now is the cleaner path forward rather than carrying this compatibility layer into the stable release.

Migration Guide

Text editing

// Before
createColumn("name", {
  headerLabel: "Name",
  editMode: "text",
  onChange: (value, rowId) => save(value, rowId),
});

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

Select editing

The options shape changed from { value, label } to { id, label } (matching SelectMenuOption), and onChange now receives a SelectMenuOption | null instead of a raw value.
// Before
createColumn("status", {
  headerLabel: "Status",
  editMode: "select",
  options: [
    { value: "open", label: "Open" },
    { value: "closed", label: "Closed" },
  ],
  onChange: (value, rowId) => {
    // value is "open" | "closed"
    updateStatus(value, rowId);
  },
});

// After
createColumn("status", {
  headerLabel: "Status",
  editConfig: {
    mode: "select",
    options: [
      { id: "open", label: "Open" },
      { id: "closed", label: "Closed" },
    ],
    onChange: (option, rowId) => {
      // option is SelectMenuOption | null
      if (option) updateStatus(String(option.id), rowId);
    },
  },
});

Multiselect editing

Same option shape change as select. onChange now receives MultiSelectMenuOption[] instead of a raw value array.
// Before
createColumn("tags", {
  headerLabel: "Tags",
  editMode: "multiselect",
  options: [
    { value: "frontend", label: "Frontend" },
    { value: "backend", label: "Backend" },
  ],
  onChange: (value, rowId) => {
    // value is string[]
    updateTags(value, rowId);
  },
});

// After
createColumn("tags", {
  headerLabel: "Tags",
  editConfig: {
    mode: "multiselect",
    options: [
      { id: "frontend", label: "Frontend" },
      { id: "backend", label: "Backend" },
    ],
    onChange: (options, rowId) => {
      // options is MultiSelectMenuOption[]
      updateTags(options.map((o) => String(o.id)), rowId);
    },
  },
});

Boolean editing

Boolean editing was only available via editConfig, so no migration is needed if you were already using editConfig. If you were using legacy editMode with a top-level onChange for boolean columns, move the onChange into editConfig:
// After
createColumn("active", {
  headerLabel: "Active",
  editConfig: {
    mode: "boolean",
    onChange: (value, rowId) => save(value, rowId),
  },
});

Breaking Changes

These changes require code updates before upgrading. The previous API is no longer supported.
The following changes are breaking:
  • editMode has been removed from ColumnDef<T> — Use editConfig.mode instead
  • Top-level onChange has been removed from ColumnDef<T> — Use editConfig.onChange instead
  • Top-level options has been removed from ColumnDef<T> — Use editConfig.options instead
  • TanStack column meta.onChange and meta.options have been removed — Use meta.editConfig instead

Why breaking?

The recent select and multiselect cell changes (switching to SelectMenuSync / MultiSelectMenuSync) made the legacy compatibility adapters cumbersome to maintain. Since DataTable is a beta component, we chose to remove the legacy API rather than continue adapting it to each internal change.
Last modified on April 2, 2026