Search by

packstub / filament-kanban

icaliman

A fast, simple Kanban board for Filament: optimistic drag and drop, per-user columns and drop rules, a focus mode without the sidebar, and cards shaped in one closure.

Package info

github.com/packstub/filament-kanban

Documentation

pkg:composer/packstub/filament-kanban

Fund package maintenance!

icaliman

Statistics

Installs: 4

Dependents: 0

Suggesters: 0

Stars: 1

Open Issues: 0

v0.2.0 2026-09-30 14:01 UTC

This package is auto-updated.

Last update: 2026-09-30 16:19:34 UTC


README

Filament Kanban: a fast Kanban board for Filament

Latest Version on Packagist Tests Total Downloads License Sponsor

A fast, simple Kanban board for Filament: a drop lands at once, the server checks every rule, and your Filament actions work on every card.

Features

  • Instant drag and drop: a drop moves the card at once and asks the server afterwards. If the server says no, the card slides back and the reason is shown. The board never re-renders on a move.
  • Rules on the server: per-user columns, drop rules, read-only columns and WIP limits. Impossible targets dim while you drag, and the server enforces the same rules.
  • Filament actions on cards: edit in a modal or slide-over, delete, or run your own actions from a card's menu; one click opens a card.
  • Create in a column: a "+" on each column opens your create form with the column already set.
  • Columns from an enum: one line turns a backed enum into columns, with labels and colours from HasLabel and HasColor.
  • Column summaries: a sum or an average under each column title, kept current after every move.
  • Cards in one closure: a reference, a title, an amount, badges, a line of meta and avatars. Cards are drawn in the browser from JSON, so hundreds of them stay light.
  • Search and filters: instant search as you type (refined on the server), select filters, paging per column with infinite scroll.
  • Focus mode: the board page hides the panel's sidebar so the columns get the whole width.
  • Events: a CardMoved Laravel event, a browser event, and optional polling for shared boards.
  • Folding and hiding columns (remembered per user), "Move to…" for touch and keyboards, manual ordering.
  • Dark mode ready and translatable (English, Romanian, Russian, German).

Compatibility

Plugin Filament Laravel PHP
0.x 4.x, 5.x 12.x, 13.x 8.3+

Installation

composer require packstub/filament-kanban
php artisan filament:assets

No config, no migrations. The board uses the SortableJS that Filament already ships and plain CSS on your panel's palette, so there is nothing to add to your theme.

Add a board page next to a resource (or a standalone KanbanPage) and describe the board in one method:

use App\Enums\DealStage;
use App\Models\Deal;
use Packstub\Kanban\Board;
use Packstub\Kanban\Card;
use Packstub\Kanban\Pages\KanbanResourcePage;

class DealBoard extends KanbanResourcePage
{
    protected static string $resource = DealResource::class;

    public function kanban(Board $board): Board
    {
        return $board
            ->query(fn () => Deal::query()->with('owner'))
            ->columnAttribute('stage')
            ->columns(DealStage::class)
            ->card(fn (Deal $deal) => Card::make()
                ->eyebrow($deal->reference)
                ->title($deal->company)
                ->aside(Number::currency($deal->amount, 'USD'))
                ->meta([$deal->title, $deal->close_at?->format('M j')])
                ->avatar($deal->owner?->avatar_url, $deal->owner?->name));
    }
}

Register it in the resource's getPages(): 'board' => DealBoard::route('/board').

Read more: Installation, including a board inside any Livewire component.

Instant moves

A pipeline board in Filament: six columns with totals, cards with amounts, badges and owners

A drop moves the card in the browser at once and asks the server with a small renderless call. The server loads the record through the board's query() and checks the column rules; your move logic runs; the card flashes when it is saved, or slides back with the reason when it is refused. Give your own logic to moveUsing() and throw to refuse:

->moveUsing(function (Deal $deal, string $to, string $from) {
    if ($to === 'won' && ! $deal->contract_signed) {
        throw new \RuntimeException('Attach the signed contract first.');
    }

    $deal->update(['stage' => $to]);
})

Scope the query (tenant, team, date window) and every read, move and action goes through it.

Read more: Moves and events.

Columns and rules

Dragging a card: the columns it may go to are highlighted, the others dimmed

Build the columns from an enum, or list them. Every rule takes a closure and is evaluated once per request, never per card:

->columns([
    Column::make('todo')->label('To do')->color('sky'),
    Column::make('doing')->color('amber')->accepts(['todo'])->limit(5),
    Column::make('review')->color('violet')
        ->accepts(['doing'])
        ->visible(fn () => auth()->user()->isReviewer()),
    Column::make('done')->color('emerald')
        ->accepts(['review'])
        ->droppable(fn () => auth()->user()->can('ship'))
        ->collapsed(),
])
Method What it does
visible() / hidden() Leave the column out for this user: its cards are not loaded and nothing moves into or out of it.
accepts([...]) The columns a card may come from. null (default) means anywhere.
droppable(), draggable(), readOnly() Who may drop in and drag out.
limit() A WIP limit: 3/5 in the header, and a full column refuses moves and new cards.
creatable() Whether the column offers "+" for the create action.
collapsed(), sortBy() Start folded; this column's order.

Read more: Columns.

Card actions

A card's menu with Edit, Mark as hot and Delete above "Move to"

Your Filament actions work on cards, with their forms, modals, slide-overs and confirmations. The card's record is the action's record, loaded through the board's query, and the board refreshes once an action has run. cardAction() picks the one a click on the card runs:

->cardActions([
    EditAction::make()->schema(DealResource::fields())->slideOver(),
    Action::make('hot')->icon(Heroicon::OutlinedFire)->action(fn (Deal $record) => $record->update(['is_hot' => true])),
    DeleteAction::make(),
])
->cardAction('edit')

Editing a deal in a slide-over, opened by clicking its card

Read more: Actions.

Create in a column

The "New deal" slide-over, opened from the Qualified column

createAction() puts a "+" in each droppable column's header. The form opens, and the new record gets that column:

->createAction(CreateAction::make()->label('New deal')->schema([...])->slideOver())

Hidden, read-only, full and creatable(false) columns get no "+", and the server refuses a create into them.

Read more: Actions.

Summaries and limits

A line under each column title, from the column's query with the current search and filters, refreshed after every move:

->summarize(fn (Builder $query) => Number::currency($query->sum('amount'), 'USD'))

Read more: Columns.

Cards

Card::make() with eyebrow() (small monospaced line), title(), aside() (right-aligned: an amount, a date), badge($label, $color, $condition), meta([...]) (empty parts dropped), avatar($url, $name) (initials without a picture), accent($color) (a coloured left edge), url(), searchText() and actions([...]).

Read more: Cards.

Search, filters and paging

Searching the board: the matching cards of every column, with the totals following

->searchable(['reference', 'company', 'owner.name'])
->filters([
    Filter::make('owner_id')->label('Owner')->options(fn () => User::pluck('name', 'id')->all()),
])
->perColumn(50)

Typing filters the loaded cards at once, then the server brings the matching cards of every column. Each column loads its cards in pages as you scroll.

Read more: Configuration.

Configuration

$board
    ->reorderable('sort')   // users order cards inside a column
    ->sortBy('due_at')      // default order (a column can override it)
    ->poll('30s')           // pick up other people's changes
    ->focusMode(false)      // keep the panel's sidebar on the board page
    ->key('deals');         // where the browser remembers folded and hidden columns

The board in dark mode

The stylesheet is plain CSS on Filament's colour variables and follows dark mode. Restyle it with the --pk-* variables on .pk (--pk-ring, --pk-col-width, --pk-card-bg, --pk-radius, …) from your theme, prefixed so they win over the plugin's stylesheet (.fi-body .pk { … }).

Read more: Configuration, the full fluent API.

Documentation

The same pages live in the docs/ directory of this repository.

Testing

composer test

Changelog

See the changelog.

Security vulnerabilities

Please e-mail support@packstub.dev rather than opening a public issue.

Credits

License

MIT. See the license file.