Skip to content

Column Move With Groups

ColumnMoveAdvancedPlugin enables drag-and-drop for grouped column headers. It builds on RevoGrid's core column move behavior and adds the group-aware rules needed for ColumnGrouping columns.

Use it when your grid has nested column groups and users need to reorder whole header bands without manually rebuilding the columns array.

Source code
TypeScript ts
import {
  ColumnMoveAdvancedPlugin,
  RowOddPlugin,
  columnTypeRenderer,
} from '@revolist/revogrid-pro';
import { defineCustomElements } from '@revolist/revogrid/loader';
defineCustomElements();

const grid = document.querySelector('revo-grid');
if (grid) {
  grid.colSize = 120;
  grid.source = [
    {
      firstName: 'John',
      lastName: 'Doe',
      age: 30,
      street: '123 Main St',
      city: 'New York',
      country: 'USA',
      email: '[email protected]',
      phone: '123-456-7890',
      mobile: '098-765-4321',
    },
    {
      firstName: 'Jane',
      lastName: 'Smith',
      age: 28,
      street: '456 Oak Ave',
      city: 'São Paulo',
      country: 'Brazil',
      email: '[email protected]',
      phone: '234-567-8901',
      mobile: '987-654-3210',
    },
    {
      firstName: 'Bob',
      lastName: 'Johnson',
      age: 35,
      street: '789 Pine Rd',
      city: 'Madrid',
      country: 'Spain',
      email: '[email protected]',
      phone: '345-678-9012',
      mobile: '876-543-2109',
    },
    {
      firstName: 'Alice',
      lastName: 'Williams',
      age: 32,
      street: '321 Elm St',
      city: 'London',
      country: 'UK',
      email: '[email protected]',
      phone: '456-789-0123',
      mobile: '765-432-1098',
    },
  ];

  grid.columns = [
    {
      name: 'Personal Information',
      columnTemplate: columnTypeRenderer,
      children: [
        {
          prop: 'firstName',
          name: 'First Name',
          size: 125,
        },
        {
          prop: 'lastName',
          name: 'Last Name',
        },
        {
          prop: 'age',
          name: 'Age',
        },
      ],
    },
    {
      name: 'Address',
      columnType: 'id',
      columnTemplate: columnTypeRenderer,
      children: [
        {
          prop: 'street',
          name: 'Street',
          size: 155,
        },
        {
          prop: 'city',
          name: 'City',
        },
        {
          prop: 'country',
          name: 'Country',
        },
      ],
    },
    {
      name: 'Contact',
      columnType: 'integer',
      columnTemplate: columnTypeRenderer,
      children: [
        {
          prop: 'email',
          name: 'Email',
        },
        {
          prop: 'phone',
          name: 'Phone',
        },
        {
          prop: 'mobile',
          name: 'Mobile',
        },
      ],
    },
  ];

  grid.plugins = [ColumnMoveAdvancedPlugin, RowOddPlugin];
  grid.theme = 'material';
}

React tsx
import React from 'react';
import { RevoGrid } from '@revolist/react-datagrid';
import {
  ColumnMoveAdvancedPlugin,
  RowOddPlugin,
  columnTypeRenderer,
} from '@revolist/revogrid-pro';
import { currentTheme } from '../composables/useRandomData';

const ColumnMoveWithGroups: React.FC = () => {
  const { isDark } = currentTheme();

  const source = [
    {
      firstName: 'John',
      lastName: 'Doe',
      age: 30,
      street: '123 Main St',
      city: 'New York',
      country: 'USA',
      email: '[email protected]',
      phone: '123-456-7890',
      mobile: '098-765-4321',
    },
    {
      firstName: 'Jane',
      lastName: 'Smith',
      age: 28,
      street: '456 Oak Ave',
      city: 'São Paulo',
      country: 'Brazil',
      email: '[email protected]',
      phone: '234-567-8901',
      mobile: '987-654-3210',
    },
    {
      firstName: 'Bob',
      lastName: 'Johnson',
      age: 35,
      street: '789 Pine Rd',
      city: 'Madrid',
      country: 'Spain',
      email: '[email protected]',
      phone: '345-678-9012',
      mobile: '876-543-2109',
    },
    {
      firstName: 'Alice',
      lastName: 'Williams',
      age: 32,
      street: '321 Elm St',
      city: 'London',
      country: 'UK',
      email: '[email protected]',
      phone: '456-789-0123',
      mobile: '765-432-1098',
    },
  ];

  const columns = [
    {
      name: 'Personal Information',
      columnTemplate: columnTypeRenderer,
      children: [
        {
          prop: 'firstName',
          name: 'First Name',
          size: 125,
        },
        {
          prop: 'lastName',
          name: 'Last Name',
        },
        {
          prop: 'age',
          name: 'Age',
        },
      ],
    },
    {
      name: 'Address',
      columnType: 'id',
      columnTemplate: columnTypeRenderer,
      children: [
        {
          prop: 'street',
          name: 'Street',
          size: 155,
        },
        {
          prop: 'city',
          name: 'City',
        },
        {
          prop: 'country',
          name: 'Country',
        },
      ],
    },
    {
      name: 'Contact',
      columnType: 'integer',
      columnTemplate: columnTypeRenderer,
      children: [
        {
          prop: 'email',
          name: 'Email',
        },
        {
          prop: 'phone',
          name: 'Phone',
        },
        {
          prop: 'mobile',
          name: 'Mobile',
        },
      ],
    },
  ];

  const plugins = [ColumnMoveAdvancedPlugin, RowOddPlugin];

  return (
    <RevoGrid
      className="grow h-full cell-border"
      source={source}
      columns={columns}
      plugins={plugins}
      resize
      hide-attribution
      theme={isDark() ? 'darkMaterial' : 'material'}
      style={{ minHeight: '300px' }}
    />
  );
};

export default ColumnMoveWithGroups;

Vue vue
<template>
  <RevoGrid
    class="grow h-full cell-border"
    :theme="isDark ? 'darkMaterial' : 'material'"
    :columns="columns"
    :source="source"
    :plugins="plugins"
    hide-attribution
    style="min-height: 300px;"
  />
</template>

<script setup lang="ts">
import { ref } from 'vue';
import RevoGrid from '@revolist/vue3-datagrid';
import {
  ColumnMoveAdvancedPlugin,
  RowOddPlugin,
  columnTypeRenderer,
} from '@revolist/revogrid-pro';
import { currentThemeVue } from '../composables/useRandomData';

const { isDark } = currentThemeVue();

const source = ref([
  {
    firstName: 'John',
    lastName: 'Doe',
    age: 30,
    street: '123 Main St',
    city: 'New York',
    country: 'USA',
    email: '[email protected]',
    phone: '123-456-7890',
    mobile: '098-765-4321',
  },
  {
    firstName: 'Jane',
    lastName: 'Smith',
    age: 28,
    street: '456 Oak Ave',
    city: 'São Paulo',
    country: 'Brazil',
    email: '[email protected]',
    phone: '234-567-8901',
    mobile: '987-654-3210',
  },
  {
    firstName: 'Bob',
    lastName: 'Johnson',
    age: 35,
    street: '789 Pine Rd',
    city: 'Madrid',
    country: 'Spain',
    email: '[email protected]',
    phone: '345-678-9012',
    mobile: '876-543-2109',
  },
  {
    firstName: 'Alice',
    lastName: 'Williams',
    age: 32,
    street: '321 Elm St',
    city: 'London',
    country: 'UK',
    email: '[email protected]',
    phone: '456-789-0123',
    mobile: '765-432-1098',
  },
]);

const columns = ref([
  {
    name: 'Personal Information',
    columnTemplate: columnTypeRenderer,
    children: [
      {
        prop: 'firstName',
        name: 'First Name',
        size: 125,
      },
      {
        prop: 'lastName',
        name: 'Last Name',
      },
      {
        prop: 'age',
        name: 'Age',
      },
    ],
  },
  {
    name: 'Address',
    columnType: 'id',
    columnTemplate: columnTypeRenderer,
    children: [
      {
        prop: 'street',
        name: 'Street',
        size: 155,
      },
      {
        prop: 'city',
        name: 'City',
      },
      {
        prop: 'country',
        name: 'Country',
      },
    ],
  },
  {
    name: 'Contact',
    columnType: 'integer',
    columnTemplate: columnTypeRenderer,
    children: [
      {
        prop: 'email',
        name: 'Email',
      },
      {
        prop: 'phone',
        name: 'Phone',
      },
      {
        prop: 'mobile',
        name: 'Mobile',
      },
    ],
  },
]);

const plugins = [ColumnMoveAdvancedPlugin, RowOddPlugin];
</script>

Angular ts
import { Component } from '@angular/core';
import { RevoGrid } from '@revolist/angular-datagrid';
import {
  ColumnMoveAdvancedPlugin,
  RowOddPlugin,
  columnTypeRenderer,
} from '@revolist/revogrid-pro';

@Component({
  selector: 'app-column-move-with-groups',
  standalone: true,
  imports: [RevoGrid],
  template: `
    <revo-grid
      class="grow h-full cell-border"
      [source]="source"
      [columns]="columns"
      [plugins]="plugins"
      resize
      hide-attribution
      style="min-height: 300px;"
    ></revo-grid>
  `,
})
export class ColumnMoveWithGroupsComponent {
  source = [
    {
      firstName: 'John',
      lastName: 'Doe',
      age: 30,
      street: '123 Main St',
      city: 'New York',
      country: 'USA',
      email: '[email protected]',
      phone: '123-456-7890',
      mobile: '098-765-4321',
    },
    {
      firstName: 'Jane',
      lastName: 'Smith',
      age: 28,
      street: '456 Oak Ave',
      city: 'São Paulo',
      country: 'Brazil',
      email: '[email protected]',
      phone: '234-567-8901',
      mobile: '987-654-3210',
    },
    {
      firstName: 'Bob',
      lastName: 'Johnson',
      age: 35,
      street: '789 Pine Rd',
      city: 'Madrid',
      country: 'Spain',
      email: '[email protected]',
      phone: '345-678-9012',
      mobile: '876-543-2109',
    },
    {
      firstName: 'Alice',
      lastName: 'Williams',
      age: 32,
      street: '321 Elm St',
      city: 'London',
      country: 'UK',
      email: '[email protected]',
      phone: '456-789-0123',
      mobile: '765-432-1098',
    },
  ];

  columns = [
    {
      name: 'Personal Information',
      columnTemplate: columnTypeRenderer,
      children: [
        {
          prop: 'firstName',
          name: 'First Name',
          size: 125,
        },
        {
          prop: 'lastName',
          name: 'Last Name',
        },
        {
          prop: 'age',
          name: 'Age',
        },
      ],
    },
    {
      name: 'Address',
      columnType: 'id',
      columnTemplate: columnTypeRenderer,
      children: [
        {
          prop: 'street',
          name: 'Street',
          size: 155,
        },
        {
          prop: 'city',
          name: 'City',
        },
        {
          prop: 'country',
          name: 'Country',
        },
      ],
    },
    {
      name: 'Contact',
      columnType: 'integer',
      columnTemplate: columnTypeRenderer,
      children: [
        {
          prop: 'email',
          name: 'Email',
        },
        {
          prop: 'phone',
          name: 'Phone',
        },
        {
          prop: 'mobile',
          name: 'Mobile',
        },
      ],
    },
  ];

  plugins = [ColumnMoveAdvancedPlugin, RowOddPlugin] as any;
}

Import the plugin from @revolist/revogrid-pro and register it in the grid plugins array.

import { ColumnMoveAdvancedPlugin } from '@revolist/revogrid-pro';
const grid = document.createElement('revo-grid');
grid.plugins = [ColumnMoveAdvancedPlugin];
grid.columns = [
{
name: 'Personal Information',
children: [
{ prop: 'firstName', name: 'First Name' },
{ prop: 'lastName', name: 'Last Name' },
{ prop: 'age', name: 'Age' },
],
},
{
name: 'Address',
children: [
{ prop: 'street', name: 'Street' },
{ prop: 'city', name: 'City' },
{ prop: 'country', name: 'Country' },
],
},
];

ColumnMoveAdvancedPlugin extends the core ColumnMovePlugin implementation, so you do not need a separate column-move plugin entry for the common setup.

Groups use the standard RevoGrid ColumnGrouping shape: a header object with a name and a children array. Children can be regular columns or another nested group.

grid.columns = [
{
name: 'Customer',
children: [
{ prop: 'name', name: 'Name' },
{
name: 'Contact',
children: [
{ prop: 'email', name: 'Email' },
{ prop: 'phone', name: 'Phone' },
],
},
],
},
{
name: 'Location',
children: [
{ prop: 'city', name: 'City' },
{ prop: 'country', name: 'Country' },
],
},
];

Leaf columns keep their normal column behavior: data binding, templates, sorting, filtering, resizing, and editing still belong to the regular column definitions.

Dragging a group header moves all leaf columns owned by that group as one block. During the drag, the drop indicator snaps to valid group boundaries instead of arbitrary leaf-column positions.

When the move completes, the plugin remaps the internal group indexes for every group level. This is the part plain ColumnMovePlugin does not handle for grouped headers.

Dragging an individual leaf column still works, but the plugin keeps the column inside its current group at the active grouping depth. This prevents a single leaf column from being dropped into a different group and leaving the header tree inconsistent.

Group integrity

Move a group by dragging the group header. Move a single leaf column by dragging the leaf header. Leaf-column moves are constrained to the compatible group area.

The plugin uses the standard column drag event names.

| Event | When it fires | Cancelable | | --- | --- | --- | | columndragstart | Before drag state is created. The detail is the dragged column or group data. | Yes | | beforecolumndragend | Before the order is committed. The detail includes startPosition, newPosition, and moved items. | Yes | | columndragend | After a successful drag flow finishes. | No |

grid.addEventListener('columndragstart', (event) => {
const data = event.detail;
if ('children' in data) {
console.log('Group drag started:', data.name);
}
});
grid.addEventListener('beforecolumndragend', (event) => {
const { startPosition, newPosition } = event.detail;
if (!canMoveColumns(startPosition, newPosition)) {
event.preventDefault();
}
});

For framework wrappers, pass the plugin class through the wrapper's plugins prop or input.

import { RevoGrid } from '@revolist/react-datagrid';
import { ColumnMoveAdvancedPlugin } from '@revolist/revogrid-pro';
export function CustomerGrid({ rows, columns }) {
return (
<RevoGrid
source={rows}
columns={columns}
plugins={[ColumnMoveAdvancedPlugin]}
/>
);
}
<script setup lang="ts">
import { ColumnMoveAdvancedPlugin } from '@revolist/revogrid-pro';
const plugins = [ColumnMoveAdvancedPlugin];
</script>
<template>
<RevoGrid :source="rows" :columns="columns" :plugins="plugins" />
</template>
  • Works with the standard nested ColumnGrouping structure.
  • Supports nested group levels by remapping indexes across all group levels after a group move.
  • Preserves regular leaf-column drag behavior, constrained to compatible group boundaries.
  • Ignores row-header drag attempts.
  • Uses the same columndragstart, beforecolumndragend, and columndragend event names as core column moving.

Use Multi Row Headers when you need spreadsheet-style multi-level header spanning. Use Column Collapse when the same grouped headers should collapse or expand.