Skip to content

Column definitions ​

Columns are defined with an array of ColumnConfig objects passed to the columns prop.

ts
import type { ColumnConfig } from "@datagrid/vue-ui"; // or "@datagrid/react-ui"

const columns: Array<ColumnConfig<Person>> = [
  { field: "name", text: "Name" },
  { field: "address.city", text: "City" }, // nested properties
  { id: "age", text: "Age", valueGetter: ({ data }) => calculateAge(data.birthday) },
];

Passing the row type (ColumnConfig<Person>) gives you type safety in callbacks like valueGetter, cellClass or editable.

Identification ​

PropertyDescription
fieldProperty of the row data. Nested properties are separated with dots: "address.city".
idUnique id of the column. Defaults to field. Required if there is no field (e.g. computed columns).
textHeader text. Defaults to field or id.

Column ids are used by the API and in the saved state.

They also identify columns in the v-model bindings (e.g. sortedColumns, columnFilter) and in slot names (#cell-<id>).

They also identify columns in the controlled props (e.g. sortedColumns, columnFilter) and are the keys of cellRenderers / headerRenderers.

Values ​

PropertyDescription
valueGetter({ data, item, column }) => value – computes the value of a cell. Use it for calculated columns.
valueFormatter({ value, data, item, column }) => string – converts the value to the displayed text. See formatting.
valueSetter({ value, data, item, column }) => void | boolean – writes an edited value, required for computed columns that are editable.
valueParser({ value, oldValue, item, column }) => value – converts the input of an editor or of the clipboard to the type of the column.

The value is used for sorting, filtering, grouping and aggregation, the formatted text is displayed, searched by the quick filter and exported.

Features ​

All features are disabled by default and can be enabled per column (or for all columns with defaultColumn):

PropertyDescription
sortableColumn can be sorted by clicking the header. See Sorting.
filterableA filter is shown in the filter row. See Filtering.
resizeableColumn width can be changed by dragging the right header border. Double click the border to autosize.
moveableColumn can be moved by dragging the header.
groupableRows can be grouped by this column. See Grouping.
editableCells can be edited. Boolean or function. See Editing.
menuShows the column menu button (overrides the grid option columnMenu).
visiblefalse hides the column initially.

Enable only what makes sense for a column: sorting a free text column or grouping by a unique value rarely helps users. In the demo every column enables exactly one feature, the header says which one.

Source
vue
<template>
  <div class="demo">
    <div class="demo-toolbar">
      Every column enables one feature – try sorting, filtering, resizing, moving, grouping, editing and the menu.
    </div>
    <Datagrid :columns="columns" :rows="rows" :options="options" />
  </div>
</template>

<script setup lang="ts">
import { Datagrid, type ColumnConfig, type GridOptions } from "@datagrid/vue-ui";
import { createEmployees, type Employee } from "./data";

const rows = createEmployees(200);

const columns: Array<ColumnConfig<Employee>> = [
  { field: "lastName", text: "Sortable", sortable: true },
  { field: "department", text: "Filterable", filterable: true },
  { field: "city", text: "Resizeable", resizeable: true },
  { field: "role", text: "Moveable", moveable: true },
  { field: "country", text: "Groupable", groupable: true },
  { field: "salary", text: "Editable", type: "number", editable: true },
  { field: "rating", text: "Menu", type: "number", menu: true },
];

const options: GridOptions<Employee> = {
  defaultColumn: { flex: 1, minWidth: 88 },
};
</script>
tsx
import { Datagrid, type ColumnConfig, type GridOptions } from "@datagrid/react-ui";
import { createEmployees, type Employee } from "../data";

const rows = createEmployees(200);

const columns: Array<ColumnConfig<Employee>> = [
  { field: "lastName", text: "Sortable", sortable: true },
  { field: "department", text: "Filterable", filterable: true },
  { field: "city", text: "Resizeable", resizeable: true },
  { field: "role", text: "Moveable", moveable: true },
  { field: "country", text: "Groupable", groupable: true },
  { field: "salary", text: "Editable", type: "number", editable: true },
  { field: "rating", text: "Menu", type: "number", menu: true },
];

const options: GridOptions<Employee> = {
  defaultColumn: { flex: 1, minWidth: 88 },
};

export default function ColumnFeatures() {
  return (
    <div className="demo">
      <div className="demo-toolbar">
        Every column enables one feature – try sorting, filtering, resizing, moving, grouping, editing and the menu.
      </div>
      <Datagrid columns={columns} rows={rows} options={options} />
    </div>
  );
}

Hidden columns ​

Columns with visible: false are part of the grid but not displayed. Use them for details most users don't need (ids, technical fields) or for columns which are only used for grouping. Users show them again with the column chooser of the column menu, your own UI calls api.setColumnVisible(id, visible) or api.toggleColumnVisibility(id). The current visibility is api.getColumn(id).visible, the state-change event notifies about changes – so the checkboxes stay in sync with the column menu.

Source
vue
<template>
  <div class="demo">
    <div class="demo-toolbar">
      <label v-for="column in optional" :key="column.id">
        <input
          type="checkbox"
          :checked="visible[column.id]"
          @change="api?.toggleColumnVisibility(column.id)"
        />
        {{ column.text }}
      </label>
    </div>
    <Datagrid
      :columns="columns"
      :rows="rows"
      :options="options"
      @ready="onReady"
      @state-change="refresh"
    />
  </div>
</template>

<script setup lang="ts">
import { ref, shallowRef } from "vue";
import { Datagrid, type ColumnConfig, type Core, type GridOptions } from "@datagrid/vue-ui";
import { createEmployees, type Employee } from "./data";

const rows = createEmployees(200);
const api = shallowRef<Core>();

// columns the user can show and hide
const optional = [
  { id: "id", text: "ID" },
  { id: "email", text: "Email" },
  { id: "city", text: "City" },
  { id: "startDate", text: "Start date" },
  { id: "salary", text: "Salary" },
];
const visible = ref<Record<string, boolean>>({});

const columns: Array<ColumnConfig<Employee>> = [
  // hidden initially
  { field: "id", text: "ID", type: "number", width: 70, visible: false },
  { field: "firstName", text: "First name" },
  { field: "lastName", text: "Last name" },
  { field: "email", text: "Email", width: 260, visible: false },
  { field: "department", text: "Department" },
  { field: "city", text: "City" },
  { field: "startDate", text: "Start date", type: "date", visible: false },
  { field: "salary", text: "Salary", type: "number" },
];

const options: GridOptions<Employee> = {
  // the column menu contains a column chooser as well
  columnMenu: true,
  defaultColumn: { flex: 1, minWidth: 110 },
};

function onReady(core: Core) {
  api.value = core;
  refresh();
}

// read the visibility from the grid, it is also changed by the column menu
function refresh() {
  for (const { id } of optional) {
    visible.value[id] = api.value?.getColumn(id)?.visible ?? false;
  }
}
</script>
tsx
import { useRef, useState } from "react";
import { Datagrid, type ColumnConfig, type Core, type GridOptions } from "@datagrid/react-ui";
import { createEmployees, type Employee } from "../data";

const rows = createEmployees(200);

// columns the user can show and hide
const optional = [
  { id: "id", text: "ID" },
  { id: "email", text: "Email" },
  { id: "city", text: "City" },
  { id: "startDate", text: "Start date" },
  { id: "salary", text: "Salary" },
];

const columns: Array<ColumnConfig<Employee>> = [
  // hidden initially
  { field: "id", text: "ID", type: "number", width: 70, visible: false },
  { field: "firstName", text: "First name" },
  { field: "lastName", text: "Last name" },
  { field: "email", text: "Email", width: 260, visible: false },
  { field: "department", text: "Department" },
  { field: "city", text: "City" },
  { field: "startDate", text: "Start date", type: "date", visible: false },
  { field: "salary", text: "Salary", type: "number" },
];

const options: GridOptions<Employee> = {
  // the column menu contains a column chooser as well
  columnMenu: true,
  defaultColumn: { flex: 1, minWidth: 110 },
};

export default function ColumnVisibility() {
  const api = useRef<Core>(undefined);
  const [visible, setVisible] = useState<Record<string, boolean>>({});

  // read the visibility from the grid, it is also changed by the column menu
  function refresh() {
    setVisible(Object.fromEntries(optional.map(({ id }) => [id, api.current?.getColumn(id)?.visible ?? false])));
  }

  return (
    <div className="demo">
      <div className="demo-toolbar">
        {optional.map((column) => (
          <label key={column.id}>
            <input
              type="checkbox"
              checked={visible[column.id] ?? false}
              onChange={() => api.current?.toggleColumnVisibility(column.id)}
            />{" "}
            {column.text}
          </label>
        ))}
      </div>
      <Datagrid
        columns={columns}
        rows={rows}
        options={options}
        onReady={(core) => {
          api.current = core;
          refresh();
        }}
        onStateChange={refresh}
      />
    </div>
  );
}

Column widths ​

A column has a fixed width (default 100), takes a share of the free space with flex or gets the width of its content with autosize. minWidth and maxWidth limit resizing, autosizing and flex columns (the salary column here can be resized between 100 and 160 pixels). Use flex for text columns which should use the available space and fixed widths for ids, numbers and dates. Resize a column in the demo and use the buttons: api.autosizeColumns() fits all columns to their content, api.sizeColumnsToFit() distributes the free space to the flex columns again. See Pinning, moving & sizing for pinned and moveable columns.

Source
vue
<template>
  <div class="demo">
    <div class="demo-toolbar">
      <button @click="api?.autosizeColumns()">Autosize all</button>
      <button @click="api?.sizeColumnsToFit()">Size to fit</button>
      <button @click="api?.resetColumnState()">Reset</button>
    </div>
    <Datagrid :columns="columns" :rows="rows" :options="options" @ready="onReady" @state-change="refresh" />
    <code class="demo-log">{{ widths }}</code>
  </div>
</template>

<script setup lang="ts">
import { ref, shallowRef } from "vue";
import { Datagrid, type ColumnConfig, type Core, type GridOptions } from "@datagrid/vue-ui";
import { createEmployees, type Employee } from "./data";

const rows = createEmployees(200);
const api = shallowRef<Core>();
const widths = ref("");

const columns: Array<ColumnConfig<Employee>> = [
  // fixed width, can't be resized
  { field: "id", text: "ID (70)", type: "number", width: 70, resizeable: false },
  // width from the content of the rows
  { field: "lastName", text: "Name (autosize)", autosize: true },
  // share of the free space: 2 parts ...
  { field: "email", text: "Email (flex 2)", flex: 2, minWidth: 150 },
  // ... and 1 part
  { field: "department", text: "Dept. (flex 1)", flex: 1, minWidth: 90 },
  // limits for resizing and autosizing
  { field: "salary", text: "Salary (max 160)", type: "number", width: 120, minWidth: 100, maxWidth: 160 },
];

const options: GridOptions<Employee> = {
  defaultColumn: { resizeable: true },
};

function onReady(core: Core) {
  api.value = core;
  refresh();
}

function refresh() {
  widths.value = (api.value?.getLeafColumns() ?? []).map((column) => `${column.id}: ${Math.round(column.width)}px`).join(", ");
}
</script>
tsx
import { useRef, useState } from "react";
import { Datagrid, type ColumnConfig, type Core, type GridOptions } from "@datagrid/react-ui";
import { createEmployees, type Employee } from "../data";

const rows = createEmployees(200);

const columns: Array<ColumnConfig<Employee>> = [
  // fixed width, can't be resized
  { field: "id", text: "ID (70)", type: "number", width: 70, resizeable: false },
  // width from the content of the rows
  { field: "lastName", text: "Name (autosize)", autosize: true },
  // share of the free space: 2 parts ...
  { field: "email", text: "Email (flex 2)", flex: 2, minWidth: 150 },
  // ... and 1 part
  { field: "department", text: "Dept. (flex 1)", flex: 1, minWidth: 90 },
  // limits for resizing and autosizing
  { field: "salary", text: "Salary (max 160)", type: "number", width: 120, minWidth: 100, maxWidth: 160 },
];

const options: GridOptions<Employee> = {
  defaultColumn: { resizeable: true },
};

export default function ColumnSizing() {
  const api = useRef<Core>(undefined);
  const [widths, setWidths] = useState("");

  function refresh() {
    setWidths((api.current?.getLeafColumns() ?? []).map((column) => `${column.id}: ${Math.round(column.width)}px`).join(", "));
  }

  return (
    <div className="demo">
      <div className="demo-toolbar">
        <button onClick={() => api.current?.autosizeColumns()}>Autosize all</button>
        <button onClick={() => api.current?.sizeColumnsToFit()}>Size to fit</button>
        <button onClick={() => api.current?.resetColumnState()}>Reset</button>
      </div>
      <Datagrid
        columns={columns}
        rows={rows}
        options={options}
        onReady={(core) => {
          api.current = core;
          refresh();
        }}
        onStateChange={refresh}
      />
      <code className="demo-log">{widths}</code>
    </div>
  );
}

Column groups ​

Columns with children are rendered as grouped headers. Groups can be nested.

Source
vue
<template>
  <Datagrid :columns="columns" :rows="rows" :options="options" />
</template>

<script setup lang="ts">
import { Datagrid, type ColumnConfig, type GridOptions } from "@datagrid/vue-ui";
import { createEmployees, type Employee } from "./data";

const rows = createEmployees(300);

const columns: Array<ColumnConfig<Employee>> = [
  // pinned columns stay visible when scrolling horizontally
  { field: "id", text: "ID", type: "number", width: 70, pinned: "left" },
  {
    id: "person",
    text: "Person",
    // column groups have children, they are rendered as grouped headers
    children: [
      { field: "firstName", text: "First name" },
      { field: "lastName", text: "Last name" },
      { field: "email", text: "Email", width: 260 },
    ],
  },
  {
    id: "job",
    text: "Job",
    children: [
      { field: "department", text: "Department" },
      { field: "role", text: "Role" },
      {
        id: "location",
        text: "Location",
        children: [
          { field: "city", text: "City" },
          { field: "country", text: "Country" },
        ],
      },
    ],
  },
  { field: "salary", text: "Salary", type: "number" },
  { field: "bonus", text: "Bonus", type: "number" },
  { field: "rating", text: "Rating", type: "number", pinned: "right", width: 90 },
];

const options: GridOptions<Employee> = {
  columnMenu: true,
  defaultColumn: { resizeable: true, moveable: true, sortable: true, width: 140 },
};
</script>
tsx
import { Datagrid, type ColumnConfig, type GridOptions } from "@datagrid/react-ui";
import { createEmployees, type Employee } from "../data";

const rows = createEmployees(300);

const columns: Array<ColumnConfig<Employee>> = [
  // pinned columns stay visible when scrolling horizontally
  { field: "id", text: "ID", type: "number", width: 70, pinned: "left" },
  {
    id: "person",
    text: "Person",
    // column groups have children, they are rendered as grouped headers
    children: [
      { field: "firstName", text: "First name" },
      { field: "lastName", text: "Last name" },
      { field: "email", text: "Email", width: 260 },
    ],
  },
  {
    id: "job",
    text: "Job",
    children: [
      { field: "department", text: "Department" },
      { field: "role", text: "Role" },
      {
        id: "location",
        text: "Location",
        children: [
          { field: "city", text: "City" },
          { field: "country", text: "Country" },
        ],
      },
    ],
  },
  { field: "salary", text: "Salary", type: "number" },
  { field: "bonus", text: "Bonus", type: "number" },
  { field: "rating", text: "Rating", type: "number", pinned: "right", width: 90 },
];

const options: GridOptions<Employee> = {
  columnMenu: true,
  defaultColumn: { resizeable: true, moveable: true, sortable: true, width: 140 },
};

export default function ColumnGroups() {
  return <Datagrid columns={columns} rows={rows} options={options} />;
}
  • The width of a group is the sum of the widths of its visible children.
  • Resizing a group distributes the width proportionally to the children.
  • A group is hidden automatically if all children are hidden.
  • Children can only be moved within their group.

Alignment, classes and styles ​

PropertyDescription
align"left", "center", "right" or a function returning the alignment for a cell
headerAlignalignment of the header text
cellClassstring, array or function ({ value, data, item, column }) => string | string[]
cellStylestyle object or function returning a style object
headerCellClass, headerCellStyleclasses and styles for the header cell
tooltiptrue shows the formatted value as tooltip, a function returns a custom tooltip
headerTooltiptooltip text of the header

The demo combines computed values and formatting: Name, Total and Bonus % are computed with valueGetter (their headers are styled with headerCellClass), the amounts and the rate are formatted with valueFormatter, Total gets a background with cellStyle and Bonus % a color with cellClass depending on the value. Sorting (click a header) uses the computed values.

Source
vue
<template>
  <div class="demo">
    <Datagrid :columns="columns" :rows="rows" :options="options" />
  </div>
</template>

<script setup lang="ts">
import { Datagrid, formatters, type ColumnConfig, type GridOptions } from "@datagrid/vue-ui";
import { createEmployees, type Employee } from "./data";

const rows = createEmployees(200);

const euro = formatters.currency("EUR", { locale: "en-US", maximumFractionDigits: 0 });

const columns: Array<ColumnConfig<Employee>> = [
  // computed text column, sorting and filtering use the computed value
  {
    id: "name",
    text: "Name",
    flex: 1.5,
    valueGetter: ({ data }) => `${data.firstName} ${data.lastName}`,
    headerCellClass: "demo-header-computed",
  },
  { field: "salary", text: "Salary", type: "number", valueFormatter: euro },
  { field: "bonus", text: "Bonus", type: "number", valueFormatter: euro },
  // computed number column with a background depending on the value
  {
    id: "total",
    text: "Total",
    type: "number",
    valueGetter: ({ data }) => data.salary + data.bonus,
    valueFormatter: euro,
    headerCellClass: "demo-header-computed",
    cellStyle: ({ value }) => ({
      backgroundColor: `rgba(46, 160, 67, ${Math.min(0.45, Math.max(0, (value - 40000) / 250000))})`,
    }),
  },
  // computed ratio, formatted as percent and colored by class
  {
    id: "bonusRate",
    text: "Bonus %",
    type: "number",
    valueGetter: ({ data }) => data.bonus / data.salary,
    valueFormatter: formatters.percent({ maximumFractionDigits: 1 }),
    headerCellClass: "demo-header-computed",
    cellClass: ({ value }) => (value >= 0.15 ? "demo-value-high" : value < 0.05 ? "demo-value-low" : undefined),
  },
  // custom formatter function
  {
    field: "rating",
    text: "Rating",
    type: "number",
    align: "center",
    headerAlign: "center",
    valueFormatter: ({ value }) => "★".repeat(Math.round(value)) + "☆".repeat(5 - Math.round(value)),
    tooltip: ({ value }) => `${value} of 5`,
    cellClass: "demo-rating",
  },
];

const options: GridOptions<Employee> = {
  defaultColumn: { sortable: true, resizeable: true, flex: 1, minWidth: 90 },
};
</script>

<style>
.demo-header-computed {
  font-style: italic;
}
.demo-value-high {
  color: #1a7f37;
  font-weight: 600;
}
.demo-value-low {
  color: #cf222e;
}
.demo-rating {
  color: #bf8700;
  letter-spacing: 1px;
}
</style>
tsx
import { Datagrid, formatters, type ColumnConfig, type GridOptions } from "@datagrid/react-ui";
import { createEmployees, type Employee } from "../data";

const rows = createEmployees(200);

const euro = formatters.currency("EUR", { locale: "en-US", maximumFractionDigits: 0 });

const columns: Array<ColumnConfig<Employee>> = [
  // computed text column, sorting and filtering use the computed value
  {
    id: "name",
    text: "Name",
    flex: 1.5,
    valueGetter: ({ data }) => `${data.firstName} ${data.lastName}`,
    headerCellClass: "demo-header-computed",
  },
  { field: "salary", text: "Salary", type: "number", valueFormatter: euro },
  { field: "bonus", text: "Bonus", type: "number", valueFormatter: euro },
  // computed number column with a background depending on the value
  {
    id: "total",
    text: "Total",
    type: "number",
    valueGetter: ({ data }) => data.salary + data.bonus,
    valueFormatter: euro,
    headerCellClass: "demo-header-computed",
    cellStyle: ({ value }) => ({
      backgroundColor: `rgba(46, 160, 67, ${Math.min(0.45, Math.max(0, (value - 40000) / 250000))})`,
    }),
  },
  // computed ratio, formatted as percent and colored by class
  {
    id: "bonusRate",
    text: "Bonus %",
    type: "number",
    valueGetter: ({ data }) => data.bonus / data.salary,
    valueFormatter: formatters.percent({ maximumFractionDigits: 1 }),
    headerCellClass: "demo-header-computed",
    cellClass: ({ value }) => (value >= 0.15 ? "demo-value-high" : value < 0.05 ? "demo-value-low" : undefined),
  },
  // custom formatter function
  {
    field: "rating",
    text: "Rating",
    type: "number",
    align: "center",
    headerAlign: "center",
    valueFormatter: ({ value }) => "★".repeat(Math.round(value)) + "☆".repeat(5 - Math.round(value)),
    tooltip: ({ value }) => `${value} of 5`,
    cellClass: "demo-rating",
  },
];

const options: GridOptions<Employee> = {
  defaultColumn: { sortable: true, resizeable: true, flex: 1, minWidth: 90 },
};

export default function ValueGetters() {
  return (
    <div className="demo">
      <Datagrid columns={columns} rows={rows} options={options} />
      <style>{`
        .demo-header-computed {
          font-style: italic;
        }
        .demo-value-high {
          color: #1a7f37;
          font-weight: 600;
        }
        .demo-value-low {
          color: #cf222e;
        }
        .demo-rating {
          color: #bf8700;
          letter-spacing: 1px;
        }
      `}</style>
    </div>
  );
}

See Custom rendering for slots / render props and custom components.

Updating columns ​

The columns prop is watched deeply. When the definitions change, existing columns are updated and keep their state (width, sort, filter), new columns are added and removed columns disappear (together with their sort and filter).

Compute the column definitions from your application state, e.g. to show columns depending on user permissions or settings. Sort or filter a column in the demo and change the currency: the header texts and formatters are updated, the sorting stays.

Source
vue
<template>
  <div class="demo">
    <div class="demo-toolbar">
      <label><input v-model="showBonus" type="checkbox" /> Bonus column</label>
      <label><input v-model="showSalary" type="checkbox" /> Salary column</label>
      <label>
        Currency
        <select v-model="currency">
          <option>EUR</option>
          <option>USD</option>
          <option>CHF</option>
        </select>
      </label>
    </div>
    <Datagrid :columns="columns" :rows="rows" :options="options" />
  </div>
</template>

<script setup lang="ts">
import { computed, ref } from "vue";
import { Datagrid, formatters, type ColumnConfig, type GridOptions } from "@datagrid/vue-ui";
import { createEmployees, type Employee } from "./data";

const rows = createEmployees(200);
const showBonus = ref(false);
const showSalary = ref(true);
const currency = ref("EUR");

// the columns are recalculated, the grid keeps width, sort and filter of existing columns
const columns = computed<Array<ColumnConfig<Employee>>>(() => {
  const money = formatters.currency(currency.value, { maximumFractionDigits: 0 });
  return [
    { field: "firstName", text: "First name" },
    { field: "lastName", text: "Last name" },
    { field: "department", text: "Department" },
    ...(showSalary.value
      ? [{ field: "salary", text: `Salary (${currency.value})`, type: "number", valueFormatter: money }]
      : []),
    ...(showBonus.value
      ? [{ field: "bonus", text: `Bonus (${currency.value})`, type: "number", valueFormatter: money }]
      : []),
  ];
});

const options: GridOptions<Employee> = {
  defaultColumn: { sortable: true, filterable: true, resizeable: true, flex: 1 },
};
</script>
tsx
import { useMemo, useState } from "react";
import { Datagrid, formatters, type ColumnConfig, type GridOptions } from "@datagrid/react-ui";
import { createEmployees, type Employee } from "../data";

const rows = createEmployees(200);

const options: GridOptions<Employee> = {
  defaultColumn: { sortable: true, filterable: true, resizeable: true, flex: 1 },
};

export default function ColumnsDynamic() {
  const [showBonus, setShowBonus] = useState(false);
  const [showSalary, setShowSalary] = useState(true);
  const [currency, setCurrency] = useState("EUR");

  // the columns are recalculated, the grid keeps width, sort and filter of existing columns
  const columns = useMemo<Array<ColumnConfig<Employee>>>(() => {
    const money = formatters.currency(currency, { maximumFractionDigits: 0 });
    return [
      { field: "firstName", text: "First name" },
      { field: "lastName", text: "Last name" },
      { field: "department", text: "Department" },
      ...(showSalary
        ? [{ field: "salary", text: `Salary (${currency})`, type: "number", valueFormatter: money }]
        : []),
      ...(showBonus
        ? [{ field: "bonus", text: `Bonus (${currency})`, type: "number", valueFormatter: money }]
        : []),
    ];
  }, [showBonus, showSalary, currency]);

  return (
    <div className="demo">
      <div className="demo-toolbar">
        <label>
          <input type="checkbox" checked={showBonus} onChange={(e) => setShowBonus(e.target.checked)} /> Bonus column
        </label>
        <label>
          <input type="checkbox" checked={showSalary} onChange={(e) => setShowSalary(e.target.checked)} /> Salary
          column
        </label>
        <label>
          Currency{" "}
          <select value={currency} onChange={(e) => setCurrency(e.target.value)}>
            <option>EUR</option>
            <option>USD</option>
            <option>CHF</option>
          </select>
        </label>
      </div>
      <Datagrid columns={columns} rows={rows} options={options} />
    </div>
  );
}

All column properties ​

See the column options reference.

Released under the ISC License.