Skip to main content

DHTMLX Kanban. Custom template for a task card example

When the built-in card layout doesn't fit your design, you can replace it entirely with a custom HTML template. This gives you full control over card markup, styling, and which data fields appear, while still getting drag-and-drop, selection, and context menu behavior from the Kanban.

Live example

const { Kanban, template, defaultCardShape } = kanban;
const { cards, columns, rows } = getData();

const cardShape = {
    ...defaultCardShape,
    color: true,
};

// escape html characters in a string
function escapeHTML(str) {
  return str.replace(/[&<>'"]/g, tag => ({
    "&": "&amp;",
    "<": "&lt;",
    ">": "&gt;",
    "'": "&#39;",
    "\"": "&quot;"
  }[tag] || tag));
}

function cardTemplate({ cardFields, selected }) {
    const { label, color } = cardFields;

    return `
        <div class="myCard ${selected ? "selected" : ""}">
            <div class="status-color" style="background:${color}"></div>
            <div class="label">${escapeHTML(label)}</div>
            <div
                class="menu-icon"
                data-menu-id=${cardFields.id}
                data-ignore-selection="true">
                    <i class="wxi-dots-v"></i>
            </div>
        </div>
    `;
}

const board = new kanban.Kanban("#root", {
    columns,
    cards,
    cardTemplate: template(card => cardTemplate(card)),
    cardShape,
});
<!-- component container -->
<div id="root" style="height: 100%;"></div>

<!-- custom styles -->

<style>
.myCard {
        background: #fff;
		flex: 1;
		display: flex;
		align-items: center;
		padding: 10px;
        border-radius: 6px;
    }
    .selected {
		background-color: #f5f5f5;
	}
    .myCard .status-color {
        border-radius: 50%;
		flex: 0 0 30px;
		height: 30px;
    }
    .myCard .label {
		padding: 0 10px;
		font-size: 15px;
		font-weight: 500;
		text-transform: capitalize;
		flex: 1 1 auto;
	}
    .myCard .menu-icon {
		cursor: pointer;
	}
	.myCard .menu-icon:hover {
		background-color: var(--wx-background-hover);
	}
</style>

The code defines a cardTemplate function that receives { cardFields, selected } and returns an HTML string with a color dot, label, and menu icon. It's passed to the constructor as cardTemplate: template(fn). The data-menu-id attribute on the menu icon enables the built-in context menu, and data-ignore-selection="true" prevents clicking the menu from triggering card selection. The escapeHTML utility prevents XSS from user-provided card labels.

Solution overview

  1. Create a template function that accepts { cardFields, selected } and returns an HTML string
  2. Wrap it with kanban.template() and pass as cardTemplate to the constructor
  3. Use data-menu-id={cardFields.id} on elements that should trigger the context menu
  4. Use data-ignore-selection="true" on interactive elements that shouldn't trigger card selection
  5. Style cards with your own CSS classes

Key points

  • template() wrapper is required: The function must be wrapped with kanban.template()
  • data-menu-id enables context menu: Without this attribute on a clickable element, users won't be able to access the card menu
  • data-ignore-selection: Add this to buttons or icons that should be clickable without selecting the card
  • cardShape still matters: The full card object is always available in cardFields regardless of cardShape, but cardShape provides the editor with some settings and optionally data, e.g. the votes button or the user list.
  • Always escape user content: Card labels are user-provided. Use escapeHTML to prevent injection

API reference

  • cardTemplate: Custom HTML template for cards
  • template: Wraps a function for reactive template rendering
  • cardShape: Controls which card fields are available

Additional resources