Skip to content

Large Boards

Kanban renders the cards needed for the viewport rather than creating a DOM element for every record. Good sizing and lightweight card content keep scrolling and interaction predictable.

Interactive example

Navigate a larger board

This example contains 1,000 local cards.

Try it: Reveal the last task, then return to the first one.

Loading example…

What to notice: Kanban scrolls to the requested card using its public reveal API.

TypeScript source

Install the packages from Getting Started, then use this source in your app.

board-actions.ts
import type { KanbanPlugin, KanbanConfig } from '@revolist/kanban';
import type { WorkCard } from './first-board';
export function moveResearch(kanban: KanbanPlugin<WorkCard>, columnProp: string) {
return kanban.moveCards(['task-1'], { columnProp });
}
export function moveResearchToPlatform(kanban: KanbanPlugin<WorkCard>) {
return kanban.moveCards(['task-1'], { columnProp: 'todo', swimlaneId: 'platform' });
}
export function selectTodoCards(kanban: KanbanPlugin<WorkCard>) {
kanban.setSelectedCardIds(kanban.getCards().filter(card => card.status === 'todo').map(card => card.id));
}
export const workInProgress: Partial<KanbanConfig<WorkCard>> = {
columns: [{ prop: 'todo', name: 'To do' }, { prop: 'doing', name: 'In progress', wipLimit: 1 }, { prop: 'done', name: 'Done' }],
wipBehavior: 'block',
};
export const startBeforeDone: Partial<KanbanConfig<WorkCard>> = {
rules: { canDrop: ({ cards, targetColumn }) => targetColumn.prop === 'done' && cards.some(card => card.status === 'todo') ? 'Start the task before marking it done.' : true },
};
export function frenchBoard(config: KanbanConfig<WorkCard>): KanbanConfig<WorkCard> {
return { ...config, locale: 'fr-FR', columns: config.columns?.map(column => ({ ...column, name: ({ todo: 'À faire', doing: 'En cours', done: 'Terminé' } as Record<string, string>)[String(column.prop)] ?? column.name })), labels: { ...config.labels, emptyColumn: 'Aucune carte', dropCardHere: 'Déposer une carte ici', moveCancelled: 'Déplacement annulé', editCard: 'Modifier la carte', deleteCard: 'Supprimer la carte' } };
}
Download board-actions.ts

Try it: scroll through the larger board and inspect cards beyond the first screen. Stable IDs keep each card connected to the same record as visible rows are recycled.

RevoGrid virtualizes workflow columns and the projected card rows. A cell contains at most one card, avoiding nested virtual scrollers and keeping card focus, drag targets, and row ownership aligned with the grid provider model.

Use cardRowHeight to reserve equal space for every card row. Choose a value that fits the number of content bands in your card template; individual swimlane definitions can override it with height. RevoGrid virtualizes those rows directly, while card identity and canonical rank remain independent of the rendered row index.

Start with Your First Board; these recipes extend its grid and config.

grid.kanban = {
columns: config.columns,
cardRowHeight: 176,
card: { titleField: 'title', descriptionField: 'description' },
};

Card content is contained within its reserved row, so an undersized custom template cannot overlap the next card. Increase cardRowHeight when adding metadata rows rather than relying on content overflow.

  • Use stable primitive IDs and finite numeric ranks.
  • Keep render hooks pure and avoid creating network requests, timers, or global listeners inside templates.
  • Put expensive derived values on the card record or memoize them outside the template.
  • Configure cardRowHeight to fit the complete managed or custom card shell while keeping every projected row uniform.
  • For known large server datasets, configure kanban.remote.total and placeholder; unloaded card rows use the same indexed skeleton pattern as Infinity Scroll.
  • In placeholder mode, known positions participate in unfiltered visible counts and total/WIP counts, while placeholders remain non-interactive. Local filters still evaluate the records currently available to the board.
  • Indexed page replacement and unfiltered card moves patch affected projection buckets, keeping interaction work independent from a full 100K projection rebuild.
  • Persist committed move batches rather than rewriting the entire dataset for every pointer update.

Virtualization limits rendered card elements to the viewport. Card identity and drag targets remain tied to canonical records as the board scrolls.

Continue with load pages, card design and limits. See the Kanban API for exact types and signatures.