harvirsidhu/filament-cards
Turn any Filament page into a card-based navigation hub. Auto-discovers Cluster/Resource pages, respects navigation config, checks authorization, and supports grouping, columns/spans, visibility rules, manual links, and optional search—ideal for settings hubs and dashboards.
To begin using harvirsidhu/filament-cards, install the package via Composer:
composer require harvirsidhu/filament-cards
Add the package's views to your Filament theme by updating theme.css:
@source '../../../../vendor/harvirsidhu/filament-cards/resources/views';
Rebuild assets:
npm run build
First Use Case:
Create a simple CardsPage to display a grid of cards linking to other Filament pages:
use Harvirsidhu\FilamentCards\Filament\Pages\CardsPage;
use Harvirsidhu\FilamentCards\CardItem;
class ControlPanel extends CardsPage
{
protected static ?string $navigationIcon = 'heroicon-o-squares-2x2';
protected static function getCards(): array
{
return [
CardItem::make(CompanySettings::class),
CardItem::make(BillingSettings::class),
];
}
}
Use discoverClusterCards() to automatically generate cards for all pages/resources in a Cluster:
class SettingsHub extends CardsPage
{
protected static ?string $cluster = Settings::class;
protected static ?int $navigationSort = -1;
protected static function getCards(): array
{
return static::discoverClusterCards();
}
}
Key Methods:
showInFilamentCards(): Override to exclude a page/resource from auto-discovery.$navigationDescription: Auto-populated in card descriptions.$filamentCardsGroup: Custom group name (defaults to $navigationGroup).Combine auto-discovered cards with manually crafted ones (e.g., external links):
protected static function getCards(): array
{
return [
...static::discoverClusterCards(),
CardGroup::make('External Links')
->schema([
CardItem::make('https://docs.example.com')
->label('Documentation')
->icon('heroicon-o-book-open')
->openUrlInNewTab(),
]),
];
}
Register cards from service providers (ideal for modular apps):
// In a ServiceProvider
FilamentCards::registerCard(
CardItem::make(ExternalTool::class)
->label('External Tool')
->icon('heroicon-o-external-link')
);
Auto-generate cards for a Resource’s pages:
class UserSettingsHub extends CardsPage
{
protected static string $resource = UserResource::class;
protected static function getCards(): array
{
return static::discoverResourceCards();
}
}
Use closures for dynamic card visibility:
CardItem::make(BillingSettings::class)
->visible(fn () => auth()->user()->can('manage-billing'))
->hidden(fn () => !auth()->user()->isAdmin());
Organize cards into collapsible groups with custom icons/descriptions:
CardGroup::make('Finance')
->icon('heroicon-o-currency-dollar')
->description('Billing and invoices')
->schema([
CardItem::make(BillingSettings::class),
CardItem::make(InvoicesResource::class),
]);
Enable live search with keywords:
class SettingsHub extends CardsPage
{
protected static bool $searchable = true;
protected static function getCards(): array
{
return [
CardItem::make(BillingSettings::class)
->searchKeywords(['invoices', 'payments']),
];
}
}
Auto-Discovery Conflicts:
shouldRegisterNavigation(): false won’t appear unless showInFilamentCards(): true is explicitly set.$excludedClusterComponents or $excludedResourcePages in CardsPage to exclude specific items.Styling Issues:
@source for the package’s views to theme.css will break card layouts.npm run build) after updating theme.css.Authorization Bypass:
canAccess(), but manually added CardItems (e.g., for URLs) bypass this.CardItems in visibility checks:
CardItem::make('/admin/tools')
->visible(fn () => auth()->user()->isAdmin())
Group Sorting:
$navigationSort. Omitting this may lead to unpredictable ordering.sort() on CardItems or use getFilamentCardsGroup() to control group order.Check Auto-Discovery:
Temporarily log the output of discoverClusterCards() or discoverResourceCards() to verify included/excluded items:
dd(static::discoverClusterCards());
Inspect Card Properties:
Use ->toArray() on a CardItem to debug its resolved attributes:
dd(CardItem::make(CompanySettings::class)->toArray());
Search Bar Not Working:
Ensure $searchable = true on the CardsPage and that searchKeywords() is populated for relevant cards.
Custom Discovery Logic:
Override discoverClusterCards() or discoverResourceCards() in a subclass to filter items dynamically:
protected static function discoverClusterCards(): array
{
return parent::discoverClusterCards()
->reject(fn ($card) => str_contains($card->getUrl(), 'legacy'));
}
Dynamic Card Registration:
Extend the FilamentCards facade to add cards from packages:
FilamentCards::extend(function (FilamentCards $cards) {
$cards->registerCard(
CardItem::make('https://example.com/docs')
->label('Docs')
->icon('heroicon-o-document-text')
);
});
Custom Card Rendering: Publish the package’s views and modify them to change the card template:
php artisan vendor:publish --tag="filament-cards-views"
Responsive Grid Control:
Use columnSpan() with responsive arrays for adaptive layouts:
CardItem::make(CompanySettings::class)
->columnSpan([
'default' => 1,
'md' => 2,
'lg' => 3,
]);
Default Grouping:
Cards without a $filamentCardsGroup fall back to $navigationGroup. Set this explicitly to avoid misplaced cards.
Icon Fallback:
If a CardItem lacks an icon, it defaults to the page/resource’s $navigationIcon. Provide a fallback:
CardItem::make('/path')
->icon('heroicon-o-question-mark-circle')
URL Resolution:
For external URLs, always use openUrlInNewTab() to avoid breaking navigation:
CardItem::make('https://example.com')
->openUrlInNewTab();
How can I help you explore Laravel packages today?