UI Lab

    Kanban & Board Variants

    12 production-ready boards — pick one, see the live preview, copy the code

    Personal Tasks

    Simple to-do board with three lanes.

    Install with:
    npx afnoui add kanban/kanban-personal-tasks
    Personal Tasks
    To Do
    2

    Buy groceries

    low
    Apr 25

    Renew gym membership

    medium
    Doing
    1

    Read TanStack docs

    medium
    Done
    1

    Pay electricity bill

    high
    Source Code

    Required Dependencies & Files

    Install packages and copy 16 files to get this kanban running.

    Runtime deps

    npm install react react-dom lucide-react clsx tailwind-merge class-variance-authority
    • Requires shadcn/ui components: button, badge, input, label, textarea, select, dialog, scroll-area, avatar, progress, use-toast.
    • Bring `cn` from your `@/lib/utils` (clsx + tailwind-merge).
    • DnD library is bundled — no @dnd-kit dependency.
    • Import components/dnd/dnd.css once at app entry (e.g. in your global stylesheet).
    Variant-specific (regenerated per board) Shared engine (copy once)
    Variant-specific

    Your generated board component — wires the engine to your config + data + onChange hook.

    src/boards/MyTasks/MyTasks.tsx
    src/boards/MyTasks/MyTasks.tsx
    import { useState } from "react";
    import { KanbanBoard } from "@/components/kanban/KanbanBoard";
    import { boardConfig } from "./config";
    import { initialCards } from "./data";
    import { useMyTasksCardChange, useMyTasksLoadMore } from "./useCardChange";
    import type { KanbanCardData } from "@/components/kanban/types";
    
    /**
     * My Tasks
     * Personal productivity board
     *
     * Drag-and-drop is powered by the project's custom Pointer DnD library
     * (no @dnd-kit dependency). Cards persist locally — wire `useCardChange`
     * to your backend to make moves stick across reloads.
     */
    export default function MyTasks() {
      const [cards, setCards] = useState<KanbanCardData[]>(initialCards);
      const handleCardChange = useMyTasksCardChange();
      const handleLoadMore = useMyTasksLoadMore();
      return (
        <div className="space-y-4">
          <div>
            <h2 className="text-xl font-bold">{boardConfig.title}</h2>
            {boardConfig.subtitle && (
              <p className="text-sm text-muted-foreground">{boardConfig.subtitle}</p>
            )}
          </div>
          <KanbanBoard
            config={boardConfig}
            cards={cards}
            onCardsChange={setCards}
            onCardChange={handleCardChange}
            onLoadMore={handleLoadMore}
          />
        </div>
      );
    }