DHTMLX Kanban. Fixed headers, lazy rendering and column scroll example
Boards with many cards per column can become sluggish and hard to navigate. Fixed headers keep column titles visible while scrolling, lazy rendering only draws cards in the viewport for better performance, and column scroll adds a scrollbar within each column to manage overflow.
Live example
const { Kanban, defaultEditorShape } = kanban;
const board = new Kanban("#root", {
columns,
cards,
rows,
rowKey: "type",
cardShape,
editorShape: defaultEditorShape,
});
let scrollType = "default";
let renderType = "default";
let cardHeight = 200;
let fixedHeaders;
function updateConfig() {
board.setConfig({
scrollType,
renderType,
cardHeight: renderType === "lazy" && scrollType === "default" && cardHeight,
columnShape: { fixedHeaders }
})
}
function handleScrollChange(el) {
scrollType = el.value;
updateConfig();
}
function handleRenderingChange(el) {
renderType = el.value;
updateConfig();
}
function handleFixedChange(el) {
fixedHeaders = el.checked;
updateConfig();
}<!-- custom styles -->
<link rel="stylesheet" href="https://snippet.dhtmlx.com/codebase/assets/css/auxiliary_controls.css">
<style>
#root .wx-kanban{
--wx-kanban-column-height: 500px;
}
</style>
<script>
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,
avatar: "John Doe",
},
];
const cardShape = {
label: true,
description: true,
progress: true,
users: {
show: true,
values: users,
},
start_date: {
show: true,
format: "%d.%m.%Y" // !!!
},
end_date: {
show: true,
format: "%d.%m.%Y" // !!!
},
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: false,
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: new Date("01/07/2021"),
end_date: new Date("02/08/2021"),
users: [3, 2],
column: "backlog",
type: "feature",
},
{
label: "Archive the cards/boards ",
priority: 3,
color: "#58C3FE",
users: [4],
progress: 1,
column: "backlog",
type: "feature",
},
{
label: "Searching and filtering",
priority: 1,
color: "#58C3FE",
start_date: new Date("01/09/2021"),
users: [3, 1],
progress: 1,
column: "backlog",
type: "task",
},
{
label: "Set the tasks priorities",
color: "#FFC975",
start_date: new Date("12/21/2020"),
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: new Date("01/07/2021"),
users: [3, 2],
column: "inprogress",
type: "task",
},
{
label: "Integration with Gantt",
color: "#FFC975",
start_date: new Date("12/21/2020"),
users: [4],
progress: 75,
column: "inprogress",
type: "feature",
},
{
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: new Date("01/07/2021"),
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: new Date("12/9/2020"),
users: [1, 4, 3],
progress: 100,
column: "done",
type: "task",
},
];
</script>The demo uses setConfig to toggle three independent features at runtime: scrollType switches between default (page-level) and column-level scrolling, renderType enables lazy rendering that only mounts visible cards, and columnShape.fixedHeaders pins column headers at the top during scroll. When lazy rendering is active with default scroll, cardHeight must be specified so the board can calculate total scroll height without rendering all cards.
Solution overview
- Initialize the board with standard config
- Use
board.setConfig({ scrollType: "column" })for per-column scrollbars - Use
board.setConfig({ renderType: "lazy", cardHeight: 200 })for lazy rendering - Use
board.setConfig({ columnShape: { fixedHeaders: false } })to unpin headers (they are sticky by default) - Combine all three for optimal performance on large boards
Key points
cardHeightis required for lazy + default scroll: Lazy rendering with page-level scroll needs a fixed card height to calculate the scrollable area. Without it, scroll height is unknown- CSS variable for column height: Set
--wx-kanban-column-heightto control the height of the scrollable card area in each column (default is300px) fixedHeadersistrueby default: Column headers are sticky (pinned) unlesscolumnShape.fixedHeaders: falseis set explicitly- Runtime toggling: All three features can be switched on/off via
setConfigwithout reinitializing the board - Column scroll and static
cardHeightare recommended: We recommend to set staticcardHeight. However, lazy rendering with column scroll can work without a predefined card height and works mostly fine, but this feature is marked as experimental, so take note.
API reference
- scrollType: Sets the scroll behavior (default or column)
- renderType: Enables lazy rendering
- cardHeight: Fixed card height for lazy rendering calculations
- columnShape: Column appearance including fixed headers
- setConfig: Updates board configuration at runtime