Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Filament Kanban Laravel Package

packstub/filament-kanban

Filament Kanban adds drag-and-drop Kanban boards to Filament admin panels. Organize records into columns, move cards between stages, and manage workflows without leaving the panel. Simple setup for Laravel and Filament apps.

View on GitHub
Deep Wiki
Context7

Getting Started

This is a Filament board component: you describe the board in PHP (query, columns, card actions) and it renders a drag-and-drop Kanban with server-side enforcement of moves.

Minimal path to a working board:

  1. Require the package and make sure Filament's assets are rebuilt/republished if your setup needs it (php artisan filament:assets), then clear caches.
  2. Create a board class (a Filament page or widget that uses the board component) and give it the three things every board needs: a query for the records, a card renderer, and columns.
  3. Start with an enum-backed status column — it is the fastest way to see everything working:
public function board(Board $board): Board
{
    return $board
        ->query(Task::query())
        ->columns(Status::class)          // one column per enum case
        ->cardAction('edit')              // click a card → edit modal
        ->createAction(CreateAction::make()->schema([
            Forms\Components\TextInput::make('title')->required(),
        ]));
}

Where to look first: the board class you just wrote, then the enum (add HasLabel and HasColor — the board reads those for column titles and colours), then Column::limit() and summarize() for the header.

First real use case: a task/ticket board on a model with a status column. Drag a card between columns, watch the status attribute persist, click a card to edit it in a modal, hit "+" on a column to create a record pre-filled with that column's value.

Implementation Patterns

Board query is the single source of truth. Card actions are resolved through the board's query, so scoping the query (tenant, project, ->where('archived', false)) scopes what users can act on. Build the query once and let actions, WIP checks, and summaries all inherit it.

Enum columns for status workflows. columns(Status::class) or Column::fromEnum() generates one column per case with labels and colours pulled from HasLabel/HasColor. This is the pattern to default to; hand-rolled Column::make('in_progress') only when the column key isn't a straight enum case.

Card actions, two flavours. Card::actions([...]) narrows the menu per card (e.g. only show DeleteAction when the record is a draft), and the card's menu holds any Filament action you can build — EditAction, DeleteAction, or your own with a schema. cardAction('edit') is the click-through behaviour: name an action and clicking the card body runs it in a modal or slide-over. The board refreshes itself after the action completes, so you rarely need manual $this->resetTable()-style calls — but do dispatch your own refresh if the action mutates something shown outside the board.

Create-in-column. createAction(CreateAction::make()->schema([...])) adds a "+" to every droppable column and sets the column attribute on the new record for you. Use Column::creatable(false) for columns that shouldn't take new records (e.g. "Done", "Archived"). The server independently refuses creates on hidden, read-only, and full columns, so hiding the button is cosmetic, not the safeguard.

WIP limits as a workflow tool. Column::limit(5) renders 3/5 in the header and rejects both moves and creates at the boundary, server-side. Pair it with a summary so users understand why a column is full. Expect to surface the rejection message to users — decide early whether that's a notification or an inline board message.

Summaries for at-a-glance metrics. summarize(fn (Builder $query) => $query->sum('points')) gives you a line under each column title, recomputed after every move. Keep these cheap: they run per column and re-run on drag, poll, and action refresh. Prefer a single aggregate over a query per row.

Polling for multi-user boards. poll('10s') picks up other people's changes. It pauses while someone is dragging, and — importantly — a poll preserves the "Load more" state, so a user with 200 cards expanded doesn't get snapped back to page one every ten seconds. Tune the interval by board activity; 10s is aggressive for large boards with expensive summaries.

Reacting to moves. Listen for Packstub\Kanban\Events\CardMoved server-side (audit log, notifications, recalculating denormalised counters), and hook the kanban-card-moved browser event for Alpine-side reactivity (closing a detail panel, updating a counter in another widget).

Avatars on cards. Card::avatar($url, $name) puts owner/assignee faces in the card foot, with initials as the fallback when there's no picture. The instant search matches avatar names too, so "assigned to Sam" becomes searchable without extra config.

Gotchas and Tips

  • No HasActions = no actions. If your board component doesn't implement Filament's action system, card and create actions silently don't appear. If the "+" or the card menu is missing, check this before anything else.
  • WIP limits are enforced on the server. Don't assume a rejected drop means a bug in the JS — read the validation/notification path. Same for creates on hidden, read-only, and full columns: the UI hides, the server refuses.
  • The dragged card's ghost lives outside the board DOM. That's the fix in this release — Sortable's ghost is no longer evaluated by Alpine, so the "card is not defined" console spam on every drag is gone, and the ghost now keeps its background, fonts, and dark-mode colours. If you have custom CSS targeting .sortable-ghost or styling cards via a parent selector, it will no longer match the ghost; style the ghost class directly.
  • Summaries run often. Every move, every action refresh, every poll. A sum() is fine; a withCount chain across relations on a big board is not. Profile with the poll interval set to something short.
  • Poll state vs. local state. Polls and post-action refreshes preserve loaded "Load more" pages, but they will still overwrite unsaved inline edits. If you build inline editing, commit on blur rather than relying on the board to hold state.
  • Search covers more than you think. It matches card content and avatar names. If a user reports odd search results, check the assignee names before hunting the query.
  • Enum labels and colours come from the enum. A column showing a raw case value means HasLabel (or getLabel()) isn't implemented on the enum — that's the fix, not a board option.
  • Event listeners need registering in the usual Laravel way (Event::listen / $listen), and the browser event needs an Alpine x-on:kanban-card-moved.window listener. Both fire after the move persists, so don't use them to validate a move — the server-side WIP/limit checks are the validation layer.
  • Extension points worth knowing: custom Filament actions in the card menu (full schema support), per-card action lists via Card::actions(), per-column creatable(false), and the CardMoved event as the seam for anything the package doesn't do — audit trails, webhooks, Slack pings, denormalised counters on the parent project.
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
shitoudev/permissionmanager
johannesclimacus/laravel-tui
hocvt/seotools
vitalybaev/sypexgeo
amphibee/themosis-ignition
gito-me/fast-excel
frankyso/laravel-ipaymu
geolize-studio/laravel-chile
boomerangui/chat
jinas/dhivatar
store-doc/laravel-storage-driver
plus-it/laravel-translation-manager
diephp/perhaps
diephp/telegraph
osmaviation/spreadsheet
microsoft/fluentui-system-icons
mmccook/php-json-canonicalization-scheme
middlewares/firewall
qubiqx/montapacking
nyoncode/laravel-package-console