Skip to main content
DragSortTable extends ProTable with drag-and-drop functionality, allowing users to reorder table rows by dragging. It’s perfect for priority lists, task ordering, and custom sorting scenarios.

Basic Usage

import { DragSortTable } from '@ant-design/pro-components';
import type { ProColumns } from '@ant-design/pro-components';
import { useState } from 'react';

type DataType = {
  key: string;
  name: string;
  age: number;
  address: string;
};

const Demo = () => {
  const [dataSource, setDataSource] = useState<DataType[]>([
    {
      key: 'key1',
      name: 'John Brown',
      age: 32,
      address: 'New York No. 1 Lake Park',
    },
    {
      key: 'key2',
      name: 'Jim Green',
      age: 42,
      address: 'London No. 1 Lake Park',
    },
    {
      key: 'key3',
      name: 'Joe Black',
      age: 32,
      address: 'Sidney No. 1 Lake Park',
    },
  ]);

  const columns: ProColumns<DataType>[] = [
    {
      title: '排序',
      dataIndex: 'sort',
    },
    {
      title: '姓名',
      dataIndex: 'name',
    },
    {
      title: '年龄',
      dataIndex: 'age',
    },
    {
      title: '地址',
      dataIndex: 'address',
    },
  ];

  const handleDragSortEnd = (
    beforeIndex: number,
    afterIndex: number,
    newDataSource: DataType[],
  ) => {
    console.log('排序后的数据', newDataSource);
    setDataSource(newDataSource);
  };

  return (
    <DragSortTable
      headerTitle="拖拽排序"
      columns={columns}
      rowKey="key"
      search={false}
      pagination={false}
      dataSource={dataSource}
      dragSortKey="sort"
      onDragSortEnd={handleDragSortEnd}
    />
  );
};

Core Props

dragSortKey

Specifies which column should display the drag handle:
<DragSortTable
  dragSortKey="sort" // Column key that shows drag handle
  columns={[
    {
      title: '排序',
      dataIndex: 'sort', // Must match dragSortKey
    },
    // ... other columns
  ]}
/>
The column with dataIndex matching dragSortKey will automatically display a drag handle icon. You don’t need to render anything in that column.

onDragSortEnd

Callback fired when drag sorting completes:
onDragSortEnd={(beforeIndex, afterIndex, newDataSource) => {
  // beforeIndex: original position of the dragged row
  // afterIndex: new position of the dragged row
  // newDataSource: complete array with new order
  
  console.log(`Moved from ${beforeIndex} to ${afterIndex}`);
  setDataSource(newDataSource);
  
  // Optionally save to server
  await saveOrder(newDataSource);
}}

Custom Drag Handle

Customize the appearance of the drag handle:
import { MenuOutlined } from '@ant-design/icons';

const dragHandleRender = (rowData: DataType, index: number) => (
  <>
    <MenuOutlined style={{ cursor: 'grab', color: 'gold' }} />
    &nbsp;{index + 1} - {rowData.name}
  </>
);

<DragSortTable
  columns={columns}
  dragSortKey="sort"
  dragSortHandlerRender={dragHandleRender}
  onDragSortEnd={handleDragSortEnd}
/>
The drag handle render function receives rowData and index, allowing you to create context-aware drag handles.

Working with Remote Data

Integrate drag sorting with server data:
import type { ActionType } from '@ant-design/pro-components';
import { useRef } from 'react';

const Demo = () => {
  const actionRef = useRef<ActionType>();
  
  const handleDragSortEnd = async (
    beforeIndex: number,
    afterIndex: number,
    newDataSource: DataType[],
  ) => {
    console.log('Updating server with new order...');
    
    try {
      // Send new order to server
      await fetch('/api/update-order', {
        method: 'POST',
        body: JSON.stringify(newDataSource),
      });
      
      // Reload table data from server
      actionRef.current?.reload();
      message.success('排序已保存');
    } catch (error) {
      message.error('保存失败');
    }
  };

  return (
    <DragSortTable
      actionRef={actionRef}
      headerTitle="使用 request 获取数据源"
      columns={columns}
      rowKey="key"
      request={async () => {
        const response = await fetch('/api/data');
        const data = await response.json();
        return {
          data: data.list,
          success: true,
          total: data.total,
        };
      }}
      dragSortKey="sort"
      onDragSortEnd={handleDragSortEnd}
    />
  );
};
1
User Drags Row
2
The user drags a row to a new position in the table.
3
onDragSortEnd Called
4
The callback receives the new order and you can update local state or send to server.
5
Reload Data
6
Optionally reload data from the server to ensure consistency.

Styling the Drag Handle

The drag handle uses the default HolderOutlined icon. You can style it using CSS:
import { HolderOutlined } from '@ant-design/icons';

const dragHandleRender = () => (
  <HolderOutlined 
    style={{ 
      cursor: 'grab',
      color: '#999',
      fontSize: 16,
    }} 
  />
);
Or use a completely custom component:
const dragHandleRender = (rowData: DataType, index: number) => (
  <div
    style={{
      cursor: 'grab',
      padding: '4px 8px',
      background: '#f0f0f0',
      borderRadius: 4,
    }}
  >
    ⋮⋮ {index + 1}
  </div>
);

Custom Render in Sort Column

If you want to render custom content alongside the drag handle:
const columns: ProColumns<DataType>[] = [
  {
    title: '排序',
    dataIndex: 'sort',
    render: (dom, rowData, index) => {
      return (
        <span className="customRender">
          自定义Render[{rowData.name}-{index}]
        </span>
      );
    },
  },
  // ... other columns
];

<DragSortTable
  columns={columns}
  dragSortKey="sort"
/>
When you provide a custom render function for the drag sort column, your custom content will be displayed instead of the default drag handle, but the drag functionality remains active.

Common Use Cases

Priority Task List

type Task = {
  id: string;
  priority: number;
  title: string;
  status: string;
};

const TaskList = () => {
  const [tasks, setTasks] = useState<Task[]>(initialTasks);

  const columns: ProColumns<Task>[] = [
    {
      title: '优先级',
      dataIndex: 'priority',
      width: 80,
    },
    {
      title: '任务名称',
      dataIndex: 'title',
    },
    {
      title: '状态',
      dataIndex: 'status',
      valueEnum: {
        pending: { text: '待处理', status: 'Default' },
        processing: { text: '进行中', status: 'Processing' },
        done: { text: '已完成', status: 'Success' },
      },
    },
  ];

  const handleDragEnd = (before, after, newDataSource) => {
    // Update priority based on new position
    const updated = newDataSource.map((item, index) => ({
      ...item,
      priority: index + 1,
    }));
    setTasks(updated);
    savePriorities(updated);
  };

  return (
    <DragSortTable
      headerTitle="任务优先级管理"
      columns={columns}
      dataSource={tasks}
      rowKey="id"
      dragSortKey="priority"
      onDragSortEnd={handleDragEnd}
      pagination={false}
    />
  );
};
type MenuItem = {
  key: string;
  label: string;
  icon?: string;
  order: number;
};

const MenuManager = () => {
  const [menuItems, setMenuItems] = useState<MenuItem[]>([]);

  const columns: ProColumns<MenuItem>[] = [
    {
      title: '排序',
      dataIndex: 'order',
      width: 60,
    },
    {
      title: '图标',
      dataIndex: 'icon',
      width: 60,
      render: (icon) => <Icon type={icon} />,
    },
    {
      title: '菜单名称',
      dataIndex: 'label',
    },
  ];

  return (
    <DragSortTable
      headerTitle="菜单排序"
      columns={columns}
      dataSource={menuItems}
      rowKey="key"
      dragSortKey="order"
      onDragSortEnd={(before, after, newData) => {
        setMenuItems(newData);
        updateMenuOrder(newData);
      }}
    />
  );
};

Combining with Other Features

DragSortTable supports most ProTable features:
<DragSortTable
  columns={columns}
  dragSortKey="sort"
  onDragSortEnd={handleDragSortEnd}
  
  // Search (though usually disabled for drag tables)
  search={false}
  
  // Toolbar
  toolBarRender={() => [
    <Button key="save">保存排序</Button>,
  ]}
  
  // Selection
  rowSelection={{
    onChange: (keys) => console.log(keys),
  }}
  
  // Actions
  options={{
    reload: true,
    density: true,
  }}
/>
For the best user experience with drag sorting, consider disabling pagination or using it with smaller datasets. Dragging across pages can be confusing.

Accessibility

The drag handle is keyboard accessible by default. Users can:
  1. Tab to focus the drag handle
  2. Press Space to grab the row
  3. Use arrow keys to move the row
  4. Press Space again to drop
  5. Press Escape to cancel

Performance Considerations

For large datasets:
const handleDragEnd = useCallback(
  (before, after, newDataSource) => {
    // Use callback to avoid recreating function
    setDataSource(newDataSource);
  },
  [],
);

<DragSortTable
  dataSource={dataSource}
  onDragSortEnd={handleDragEnd}
  // Enable virtualization for large lists
  scroll={{ y: 600 }}
  pagination={false}
/>

Build docs developers (and LLMs) love