npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@u40butch/kanban

v1.0.1

Published

Enterprise-grade Kanban Board component for Vue 3 with PrimeVue 4 integration

Readme

@incoder/kanban

Enterprise-grade Kanban Board component for Vue 3 with PrimeVue 4 integration

NPM Version License: MIT

✨ Features

  • 🎯 Vue 3 Composition API - Built with modern Vue 3 and TypeScript
  • 🎨 PrimeVue 4 Integration - Uses new preset-based theming system
  • 🚀 Laravel Compatible - Works seamlessly with Laravel + Inertia.js
  • 🔄 Drag & Drop - Smooth card and column reordering
  • 🏊 Swimlanes Support - Organize cards in horizontal swimlanes
  • 🎪 Flexible Slots - Customize every part of the board
  • 📦 Fully Typed - Complete TypeScript support with exported types
  • 🪶 Lightweight - Tree-shakable with minimal dependencies
  • Accessible - ARIA roles and keyboard navigation
  • 🎭 Controlled/Uncontrolled - Works both ways

📦 Installation

npm install @incoder/kanban
# or
yarn add @incoder/kanban
# or
pnpm add @incoder/kanban

Peer Dependencies

This package requires the following peer dependencies:

{
  "vue": "^3.5.0",
  "primevue": "^4.0.0"
}

Note: This library is fully compatible with Laravel + Inertia.js projects using Vue 3 and PrimeVue 4.

📖 Laravel Developers: See LARAVEL.md for complete Laravel + Inertia.js integration guide with backend examples.

🚀 Quick Start

1. Import the Component and Styles

import { KanbanBoard } from "@incoder/kanban";
import "@incoder/kanban/style.css";
import type { KanbanColumn, KanbanCard } from "@incoder/kanban";

2. Setup Your Data

import { ref } from "vue";

const columns = ref<KanbanColumn[]>([
  { id: "todo", title: "To Do", order: 0 },
  { id: "doing", title: "In Progress", order: 1 },
  { id: "done", title: "Done", order: 2 },
]);

const cards = ref<KanbanCard[]>([
  {
    id: 1,
    columnId: "todo",
    title: "Implement authentication",
    description: "Add JWT-based auth system",
    order: 0,
  },
  {
    id: 2,
    columnId: "doing",
    title: "Design dashboard",
    order: 0,
  },
]);

3. Use the Component

<template>
  <KanbanBoard
    v-model:columns="columns"
    v-model:cards="cards"
    @card-moved="handleCardMoved"
  />
</template>

📚 API Reference

KanbanBoard Props

| Prop | Type | Default | Description | | ----------- | -------------------- | ------------ | ----------------------------- | | columns | KanbanColumn[] | required | Array of column definitions | | cards | KanbanCard[] | required | Array of card data | | swimlanes | KanbanSwimlane[] | [] | Optional swimlane definitions | | options | KanbanBoardOptions | {} | Board configuration options | | loading | boolean | false | Show loading overlay |

KanbanBoardOptions

interface KanbanBoardOptions {
  // Drag & Drop
  enableDragDrop?: boolean; // Default: true
  enableColumnDrag?: boolean; // Default: false
  enableCardReordering?: boolean; // Default: true

  // Swimlanes
  enableSwimlanes?: boolean; // Default: false
  groupBy?: string; // Field to group by

  // UI Behavior
  horizontalScroll?: boolean; // Default: true
  showEmptyColumns?: boolean; // Default: true
  showCardCount?: boolean; // Default: true
  showWipLimit?: boolean; // Default: true

  // Styling
  columnWidth?: number | string; // Default: '300px'
  boardClass?: string;
  columnClass?: string;
  cardClass?: string;
}

KanbanColumn

interface KanbanColumn {
  id: string | number;
  title: string;
  order: number;

  // Optional
  color?: string; // Header accent color
  icon?: string; // PrimeIcons class
  collapsed?: boolean;
  hidden?: boolean;
  wipLimit?: number; // Work-in-progress limit
  draggable?: boolean;
  acceptsCards?: boolean;
  metadata?: Record<string, any>;
}

KanbanCard

interface KanbanCard {
  id: string | number;
  columnId: string | number;
  title: string;
  order: number;

  // Optional
  description?: string;
  tags?: KanbanTag[];
  assignees?: KanbanUser[];
  priority?: CardPriority;
  dueDate?: Date | string;
  swimlaneId?: string | number;
  draggable?: boolean;
  metadata?: Record<string, any>;
}

Events

| Event | Payload | Description | | ---------------- | ------------------- | --------------------------- | | update:columns | KanbanColumn[] | Emitted when columns change | | update:cards | KanbanCard[] | Emitted when cards change | | card-moved | CardMoveEvent | Card moved between columns | | column-moved | ColumnMoveEvent | Column reordered | | card-click | CardClickEvent | Card clicked | | card-update | CardUpdateEvent | Card data updated | | column-update | ColumnUpdateEvent | Column data updated | | board-change | BoardChangeEvent | Board state changed | | drag-start | DragStartEvent | Drag operation started | | drag-end | DragEndEvent | Drag operation ended |

Slots

| Slot Name | Props | Description | | --------------- | ------------------------------------- | ------------------------------- | | toolbar | { columns, cards, swimlanes } | Board toolbar area | | column-header | { column, cardCount, isOverLimit } | Custom column header | | column-footer | { column, cardCount } | Custom column footer | | card | { card, column, index, isDragging } | Custom card rendering | | card-overlay | { card, column } | Overlay on card (e.g., actions) | | empty-column | { column } | Empty column placeholder |

🎨 Customization Examples

Custom Card Rendering

<KanbanBoard v-model:columns="columns" v-model:cards="cards">
  <template #card="{ card, column }">
    <div class="custom-card">
      <h3>{{ card.title }}</h3>
      <p>{{ card.description }}</p>
      <button @click="editCard(card)">Edit</button>
    </div>
  </template>
</KanbanBoard>

Custom Column Header

<KanbanBoard v-model:columns="columns" v-model:cards="cards">
  <template #column-header="{ column, cardCount, isOverLimit }">
    <div class="custom-header">
      <h3>{{ column.title }}</h3>
      <span :class="{ 'text-danger': isOverLimit }">
        {{ cardCount }} cards
      </span>
      <button @click="addCard(column.id)">+</button>
    </div>
  </template>
</KanbanBoard>

Board Toolbar

<KanbanBoard v-model:columns="columns" v-model:cards="cards">
  <template #toolbar="{ columns, cards }">
    <div class="toolbar">
      <h2>Project Board</h2>
      <div>{{ cards.length }} total cards</div>
      <button @click="exportData">Export</button>
    </div>
  </template>
</KanbanBoard>

🏊 Swimlanes

Enable swimlanes to group cards horizontally:

const swimlanes = ref<KanbanSwimlane[]>([
  { id: "urgent", title: "Urgent", order: 0 },
  { id: "normal", title: "Normal", order: 1 },
]);

const options = ref({
  enableSwimlanes: true,
});

// Cards with swimlane assignment
const cards = ref<KanbanCard[]>([
  {
    id: 1,
    columnId: "todo",
    swimlaneId: "urgent",
    title: "Critical bug fix",
    order: 0,
  },
]);
<KanbanBoard
  v-model:columns="columns"
  v-model:cards="cards"
  v-model:swimlanes="swimlanes"
  :options="options"
/>

🎯 Advanced Features

WIP Limits

Set work-in-progress limits on columns:

const columns = ref<KanbanColumn[]>([
  {
    id: "in-progress",
    title: "In Progress",
    order: 1,
    wipLimit: 3, // Maximum 3 cards
  },
]);

The column header will highlight when over the limit.

Card Priority

Use the CardPriority enum for priority indicators:

import { CardPriority } from "@incoder/kanban";

const card: KanbanCard = {
  id: 1,
  columnId: "todo",
  title: "Fix critical bug",
  priority: CardPriority.HIGHEST,
  order: 0,
};

Tags and Assignees

const card: KanbanCard = {
  id: 1,
  columnId: "todo",
  title: "Implement feature",
  tags: [
    { id: 1, label: "Frontend", color: "#3b82f6" },
    { id: 2, label: "Urgent", color: "#ef4444" },
  ],
  assignees: [{ id: 1, name: "John Doe", avatar: "/avatars/john.jpg" }],
  order: 0,
};

Disable Drag for Specific Cards/Columns

const card: KanbanCard = {
  id: 1,
  columnId: "done",
  title: "Locked card",
  draggable: false, // This card cannot be dragged
  order: 0,
};

const column: KanbanColumn = {
  id: "done",
  title: "Done",
  acceptsCards: false, // Cannot drop cards here
  order: 2,
};

🎭 Event Handling

Handle Card Moves

const handleCardMoved = (event: CardMoveEvent) => {
  console.log(`Card ${event.card.title} moved`);
  console.log(`From: ${event.fromColumnId} to ${event.toColumnId}`);

  // Make API call to persist the change
  await api.moveCard(event.card.id, event.toColumnId);
};
<KanbanBoard
  v-model:columns="columns"
  v-model:cards="cards"
  @card-moved="handleCardMoved"
/>

Handle Card Clicks

const handleCardClick = (event: CardClickEvent) => {
  // Open edit dialog
  openCardDialog(event.card);
};

🎨 Theming

The component automatically inherits your PrimeVue theme. No additional configuration needed!

Using Custom PrimeVue Theme

// main.ts
import PrimeVue from "primevue/config";
import "primevue/resources/themes/lara-dark-blue/theme.css";

app.use(PrimeVue);

The Kanban board will automatically use the theme's design tokens.

Custom CSS Variables

Override specific styles if needed:

.kanban-board {
  --kanban-column-width: 350px;
  --kanban-card-gap: 1rem;
}

📱 Responsive Design

The board automatically scrolls horizontally on smaller screens. You can control this:

const options = ref({
  horizontalScroll: true, // Enable horizontal scroll
  columnWidth: "280px", // Adjust for mobile
});

♿ Accessibility

The component includes:

  • ARIA roles for board, columns, and cards
  • Keyboard navigation (Tab, Enter, Space)
  • Focus management during drag operations
  • Screen reader announcements

Keyboard Shortcuts

  • Tab - Navigate between cards
  • Enter or Space - Activate card
  • Arrow keys - Navigate cards (when focused)

🔧 TypeScript Support

All types are exported for your convenience:

import type {
  KanbanBoard,
  KanbanColumn,
  KanbanCard,
  KanbanSwimlane,
  KanbanBoardOptions,
  CardMoveEvent,
  CardClickEvent,
  BoardChangeEvent,
} from "@incoder/kanban";

🏗️ Build & Development

# Install dependencies
npm install

# Run playground
npm run dev

# Build library
npm run build

# Type check
npm run type-check

📄 License

MIT © [u40butch]

📚 Documentation

🤝 Contributing

Contributions welcome! Please read our contributing guidelines first.

🐛 Issues

Found a bug? Please open an issue.


Made with ❤️ using Vue 3 and PrimeVue 4