All pages

advanced

Custom components

Draw cards from infolist entries, or generate your own card and column header Blade components.

A board is drawn from two components: column headers and cards. To change what a card says without writing a Blade view, give the board a recordInfolist(). To change how it is put together, generate your own components and edit them like any other Blade view.

Card contents from an infolist

recordInfolist() draws the card body from infolist entries in place of the title and description fields:

php
use Asmit\AdvancedKanban\Kanban;
use Filament\Infolists\Components\IconEntry;
use Filament\Infolists\Components\TextEntry;

public function kanban(Kanban $kanban): Kanban
{
    return $kanban
        ->model(Task::class)
        ->statusField('status')
        ->recordInfolist([
            TextEntry::make('title'),
            TextEntry::make('assignee.name'),
            IconEntry::make('priority'),
        ]);
}

Entries read the record directly, so relationships such as assignee.name resolve the way they do anywhere else in Filament, and every entry method — ->label(), ->badge(), ->color(), ->visible() — works as usual.

Pass a closure to build the entries per card. Return an empty array and that card falls back to the built-in title and description markup:

php
->recordInfolist(fn (Task $record): array => $record->is_urgent
    ? [
        TextEntry::make('title')->weight(FontWeight::Bold),
        TextEntry::make('due_at')->dateTime()->color('danger'),
    ]
    : [
        TextEntry::make('title'),
    ])

Actions inside a card

Actions and action groups can sit alongside the entries. They take their record from the card they are on, so $record is the card's record, and their modals and forms behave as they do anywhere else in Filament:

php
use Filament\Actions\Action;
use Filament\Actions\ActionGroup;
use Filament\Forms\Components\TextInput;

->recordInfolist([
    TextEntry::make('title'),

    Action::make('note')
        ->action(fn (Task $record) => $record->notes()->create([...])),

    ActionGroup::make([
        Action::make('assign')
            ->schema([
                TextInput::make('name')->required(),
            ])
            ->action(fn (array $data, Task $record) => $record->assign($data['name'])),
    ]),
])

These are separate from recordActions(), which draws its actions in the card's top-right corner. Use recordActions() for the actions every card carries, and infolist actions when one belongs in the body next to the thing it acts on.

When a card action is clicked, the card's schema is rebuilt from the key the button posts back, and the record is re-fetched through the board's own query — so modifyQueryUsing() still applies and a tampered key cannot reach a record the board would not have shown.

A couple of things to know:

  • titleField() and descriptionField() are still used everywhere else — searching, for one. The infolist only replaces what the card body renders.
  • recordActions() and the locked indicator are untouched; they keep their place on the card.

Generate the components

bash
php artisan make:kanban-components --resource=YourResource

Two files land in resources/views/components/kanban/your-resource/:

  • column-header.blade.php — the column heading
  • card.blade.php — the record card

The generated card renders recordInfolist() where its title would go, so the two work together — delete that branch if you would rather write the whole body yourself.

Use them

php
<?php

namespace App\Filament\Pages;

use Asmit\AdvancedKanban\Pages\KanbanPage;

class TasksKanban extends KanbanPage
{
    protected static string $columnHeaderComponent = 'kanban.tasks.column-header';
    protected static string $cardComponent = 'kanban.tasks.card';

    public function kanban(Kanban $kanban): Kanban
    {
        return $kanban
            ->model(Task::class)
            ->statusField('status')
            ->columns([
                // Your columns here
            ]);
    }
}

Render an action inside a component

Define the action on the Kanban page:

php
use Filament\Actions\Action;
use Filament\Forms\Components\TextInput;

public function addDocsAction(): Action
{
    return Action::make('docs')
        ->schema(function (array $arguments): array {
            return [
                TextInput::make('title')->default($arguments['recordId']),
            ];
        });
}

Then evaluate it from the Blade view, passing whatever the action needs:

blade
{{ $this->evaluateAction($this->addDocsAction(), ['recordId' => $record->getKey()]) }}

Render hooks

Two render hooks let you drop extra views onto the board without touching its components:

  • KanbanRenderHook::KANBAN_SEARCH_BEFORE — content above the search bar
  • KanbanRenderHook::KANBAN_PAGE_FOOTER — content at the bottom of the page