> ## Documentation Index
> Fetch the complete documentation index at: https://anvil.servicetitan.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Remove Legacy Edit Props

> Guide for migrating from the removed editMode, onChange, and options props to editConfig.

export const VersionStatus = ({version}) => {
  const isUnreleased = version === "unreleased";
  return <Badge color={isUnreleased ? "orange" : "green"}>
      {isUnreleased ? "Unreleased" : `v${version}`}
    </Badge>;
};

<VersionStatus version="2.7.1" />

<Note>
  This guide covers changes to the **beta** DataTable component. These APIs may continue to evolve before the stable release.
</Note>

## 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. That includes structured editing — there is no supported top-level legacy API for `mode: "structured"`.

## 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

```tsx theme={null}
// 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.

```tsx theme={null}
// 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.

```tsx theme={null}
// 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`:

```tsx theme={null}
// After
createColumn("active", {
  headerLabel: "Active",
  editConfig: {
    mode: "boolean",
    onChange: (value, rowId) => save(value, rowId),
  },
});
```

## Breaking Changes

<Warning>
  These changes require code updates before upgrading. The previous API is no longer supported.
</Warning>

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.
