Skip to main content

DHTMLX Kanban. Keep sort order example

Sorting cards within columns is useful for prioritization, but by default the sort order resets when cards are added or moved. Persistent sorting keeps the chosen order intact even as the board changes, automatically placing new or moved cards in the correct position.

Live example

const { Kanban, Toolbar, defaultEditorShape } = kanban;

const board = new Kanban("#root", {
    columns,
    cards,
    rows,
    rowKey: "type",
    cardShape,
    editorShape: defaultEditorShape,
});
new Toolbar("#toolbar", { api: board.api });

setTimeout(() => board.api.exec("set-sort", { by: "label", preserve: true }));
<!-- component containers -->
<div id="toolbar"></div>
<div id="root" style="height: calc(100% - 56px);"></div>

<!-- dataset -->

<script>
function buildDateFromMD(md){const params = new URLSearchParams(typeof location !== 'undefined' ? location.search : '');const fixed = params.get('fixedYear') || window.FIXED_YEAR;const year = fixed ? Number(fixed) : new Date().getFullYear();const [m,d] = md.split('/').map(Number);return new Date(year, m - 1, d);}

    const users = [
		{
			id: 1,
			label: "Steve Smith",
			avatar: "https://snippet.dhtmlx.com/codebase/data/kanban/01/img/user-1.jpg",
		},
		{
			id: 2,
			label: "Aaron Long",
			avatar: "https://snippet.dhtmlx.com/codebase/data/kanban/01/img/user-2.jpg",
		},
		{
			id: 3,
			label: "Angela Allen",
			avatar: "https://snippet.dhtmlx.com/codebase/data/kanban/01/img/user-3.jpg",
		},
		{
			id: 4,
			label: "Angela Long",
			avatar: "https://snippet.dhtmlx.com/codebase/data/kanban/01/img/user-4.jpg",
		},
		{
			id: 5,
			label: "John Doe",
		},
	];
    
    const cardShape = {
		label: true,
		description: true,
		progress: true,
		start_date: true,
		end_date: true,
		users: {
			show: true,
			values: users,
		},
		priority: {
			show: true,
			values: [
				{ id: 1, color: "#FF5252", label: "High", value: 1 },
				{ id: 2, color: "#FFC975", label: "Medium", value: 2 },
				{ id: 3, color: "#65D3B3", label: "Low", value: 3 },
			],
		},
		color: true,
		menu: true,
		cover: true,
		attached: false,
	};
    
    const columns = [
		{
			label: "Backlog",
			id: "backlog",
		},
		{
			label: "In progress",
			id: "inprogress",
		},
		{
			label: "Testing",
			id: "testing",
		},
		{
			label: "Done",
			id: "done",
		},
	];
    
    const rows = [
		{
			label: "Feature",
			id: "feature",
		},
		{
			label: "Task",
			id: "task",
		},
	];
    
    const cards = [
		{
			label: "Integration with Angular/React",
			priority: 1,
			color: "#65D3B3",
			start_date: buildDateFromMD("01/07"),
			users: [3, 2],
			column: "backlog",
			type: "feature",
		},
		{
			label: "Archive the cards/boards ",
			priority: 3,
			color: "#58C3FE",
			users: [5],
			progress: 1,
			column: "backlog",
			type: "feature",
            start_date: buildDateFromMD("01/01"),
		},
		{
			label: "Searching and filtering",
			priority: 1,
			color: "#58C3FE",
			start_date: buildDateFromMD("01/09"),
			users: [3, 1],
			progress: 1,
			column: "backlog",
			type: "task",
		},
		{
			label: "Set the tasks priorities",
			color: "#FFC975",
			start_date: buildDateFromMD("12/21"),
			users: [4],
			progress: 75,
			column: "inprogress",
			type: "feature",
			attached: [
				{
					isCover: true,
					coverURL: "https://snippet.dhtmlx.com/codebase/data/kanban/01/img/img-1.jpg",
					previewURL: "https://snippet.dhtmlx.com/codebase/data/kanban/01/img/img-1.jpg",
					url: "https://snippet.dhtmlx.com/codebase/data/kanban/01/img/img-1.jpg",
					name: "img-1.jpg",
				},
			],
		},
		{
			label: "Custom icons",
			color: "#65D3B3",
			start_date: buildDateFromMD("01/07"),
			users: [3, 2],
			column: "inprogress",
			type: "task",
		},
		{
			label: "Integration with Gantt",
			color: "#FFC975",
			start_date: buildDateFromMD("12/21"),
			users: [4],
			progress: 75,
			column: "inprogress",
			type: "task",
		},
		{
			label: "Drag and drop",
			priority: 1,
			color: "#58C3FE",
			users: [3, 1],
			progress: 100,
			column: "testing",
			type: "feature",
		},
		{
			label: "Adding images",
			color: "#58C3FE",
			users: [4],
			column: "testing",
			type: "task",
			attached: [
				{
					isCover: true,
					coverURL: "https://snippet.dhtmlx.com/codebase/data/kanban/01/img/img-2.jpg",
					previewURL: "https://snippet.dhtmlx.com/codebase/data/kanban/01/img/img-2.jpg",
					url: "https://snippet.dhtmlx.com/codebase/data/kanban/01/img/img-2.jpg",
					name: "img-2.jpg",
				},
			],
		},
		{
			label: "Create cards and lists from the UI and from code",
			priority: 3,
			color: "#65D3B3",
			start_date: buildDateFromMD("01/07"),
			users: [3, 2],
			column: "done",
			type: "feature",
		},
		{
			label: "Draw swimlanes",
			color: "#FFC975",
			users: [2],
			column: "done",
			type: "feature",
		},
		{
			label: "Progress bar",
			priority: 1,
			color: "#FFC975",
			start_date: buildDateFromMD("12/9"),
			users: [1, 4, 3],
			progress: 100,
			column: "done",
			type: "task",
		},
	];
</script>

The code calls board.api.exec("set-sort", { by: "label", preserve: true }) after initialization (wrapped in setTimeout to ensure the board is rendered). The preserve: true flag makes the sort persistent. Any new cards added to the board or cards moved between columns are automatically sorted by label within their column. Without preserve, sorting is a one-time operation.

Solution overview

  1. Initialize the board normally
  2. Call board.api.exec("set-sort", { by: "label", preserve: true }) to apply persistent sorting
  3. Use setTimeout to ensure the board is fully rendered before applying the sort

Key points

  • preserve: true is the key: Without it, set-sort only sorts once. New or moved cards won't respect the order
  • Sort by any card field: The by parameter accepts any card property name: "label", "start_date", "priority", etc.
  • Drag reordering is overridden: With persistent sorting active, manually dragging cards to reorder within a column will be overridden by the sort. The card snaps back to its sorted position
  • setTimeout timing: The sort must be applied after the board renders. Calling it synchronously during initialization may not work

API reference

  • api.exec: Triggers internal events including set-sort

Additional resources