Columns
A column is one value of the board's column attribute (columnAttribute('status') by default). Columns appear in the order you list them.
->columnAttribute('stage')
->columns([
Column::make('lead')->color('gray'),
Column::make('qualified')->label('Qualified')->color('sky')->accepts(['lead']),
Column::make('won')->color('emerald')->accepts(['negotiation'])->sortBy('updated_at', 'desc'),
])
The label defaults to the name in headline case (in_review → "In Review"). A colour is a name (gray, slate, red, orange, amber, yellow, lime, green, emerald, teal, cyan, sky, blue, indigo, violet, purple, fuchsia, pink, rose, or your panel's primary, success, warning, danger, info) or any CSS colour.
Columns from an enum
Pass a backed enum's class and every case becomes a column, in declaration order. Labels and colours come from Filament's HasLabel and HasColor when the enum implements them (a Filament palette such as Color::Amber is drawn with its 500 shade).
->columnAttribute('stage') // cast to DealStage on the model
->columns(DealStage::class)
To adjust some of them, start from Column::fromEnum():
->columns(array_map(fn (Column $column) => match ($column->getName()) {
'negotiation' => $column->limit(5),
'lost' => $column->collapsed()->creatable(false),
default => $column,
}, Column::fromEnum(DealStage::class)))
Enum-cast attributes work everywhere: the board reads the backing value, and the default move sets it.
Who sees and moves what
Every rule takes a boolean or a closure, and is evaluated once per request, never per card.
| Method | What it does |
|---|---|
visible() / hidden() |
Leave the column out for this user: its cards are not loaded, not counted, and nothing moves into or out of it. |
droppable() |
Whether this user may drop cards in. |
draggable() |
Whether this user may drag cards out. |
readOnly() |
Shown, never changed from here: draggable(false) and droppable(false). |
accepts(['doing']) |
The columns a card may come from. null (the default) means anywhere. |
Column::make('review')
->accepts(['doing'])
->visible(fn () => auth()->user()->isReviewer()),
Column::make('done')
->accepts(['review'])
->droppable(fn () => auth()->user()->can('ship', Task::class)),
While a card is being dragged, columns it may go to are highlighted and the others are dimmed. The server checks the same rules on every move, whatever the browser sent: see Moves and events.
WIP limits
limit() caps how many cards a column holds. The header shows 3/5, turns red when the column is full, and a full column refuses both moves and new cards, on the server as well.
Column::make('doing')->limit(5),
Column::make('review')->limit(fn () => auth()->user()->team->review_capacity),
The limit counts every card of the column in the board's query, including the ones the current search or filters hide.
Summaries
summarize() on the board puts a line under every column title: a total, an average, a weighted forecast. The closure gets the column's query (with the current search and filters) and the column, and returns a string, or null for nothing.
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Support\Number;
->summarize(fn (Builder $query) => Number::currency($query->sum('amount'), 'USD'))
The summaries of both columns are refreshed after every move, and all of them after a search, a filter, an action or a poll. Each summary is one query per column.
Folding and hiding
Users fold a column to a thin strip (the fold button, or a double click on the header) and hide columns from the "Columns" menu. The browser remembers both per user and per board. collapsed() sets the starting state:
Column::make('archived')->collapsed(),
Order
Cards are ordered by the board's sortBy(), which a column can override:
->sortBy('due_at')
->columns([
// …
Column::make('done')->sortBy('completed_at', 'desc'),
])
With ->reorderable('sort') users also order cards by hand; see Moves and events.
Creating in a column
With a create action on the board, every droppable column gets a "+" in its header. creatable(false) leaves a column out:
Column::make('done')->creatable(false),