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.
Installation: Run composer require harvirsidhu/filament-cards and add the Tailwind source to your theme.css:
@source '../../../../vendor/harvirsidhu/filament-cards/resources/views';
Then rebuild assets with npm run build.
First Use Case: Create a minimal CardsPage for a Cluster front page:
use Harvirsidhu\FilamentCards\Filament\Pages\CardsPage;
class SettingsHub extends CardsPage {
protected static ?string $cluster = Settings::class;
protected static ?int $navigationSort = -1;
protected static function getCards(): array {
return static::discoverClusterCards();
}
}
This auto-generates cards for all pages/resources in the Settings cluster, respecting Filament's built-in authorization.
discoverClusterCards() to auto-populate cards from all pages/resources in a cluster. Customize visibility with showInFilamentCards() or $excludedClusterComponents.
protected static function getCards(): array {
return [
...static::discoverClusterCards(),
CardItem::make('https://docs.example.com')->label('Documentation'),
];
}
discoverResourceCards() for resource-specific hubs. Exclude pages with $excludedResourcePages.
protected static function getCards(): array {
return static::discoverResourceCards();
}
CardGroup:
CardGroup::make('Finance')
->icon('heroicon-o-currency-dollar')
->schema([
CardItem::make(BillingSettings::class)->color('success'),
]),
CardItem::make(CompanySettings::class)
->label(fn () => __('settings.company'))
->visible(fn () => auth()->user()->can('edit_company')),
$navigationDescription, $filamentCardsGroup, or getNavigationBadge() to your existing pages/resources to auto-populate card fields.
class CompanySettings extends Page {
public static ?string $navigationDescription = 'Manage company details.';
public static ?string $filamentCardsGroup = 'Business';
}
CardItem to override auto-resolved values:
CardItem::make(CompanySettings::class)
->description('Custom description')
->badge('Updated')->badgeColor('primary'),
$searchable = true on the CardsPage to add a search bar:
class SettingsHub extends CardsPage {
protected static bool $searchable = true;
}
columnSpan() for responsive layouts:
CardItem::make(CompanySettings::class)
->columnSpan(['default' => 1, 'lg' => 2]),
public function boot(): void {
FilamentCards::registerCard(
CardItem::make('https://example.com/docs')
->label('Docs')
->icon('heroicon-o-book-open'),
);
}
theme.css will break styling. Always run npm run build after installation.canAccess(), but manually added CardItems (e.g., for URLs) bypass this. Use visible() closures to enforce checks:
CardItem::make('https://example.com')
->visible(fn () => auth()->user()->can('view_external_docs')),
CardsPage itself if not excluded. The package skips the current page by default, but verify with $excludedClusterComponents if needed.searchKeywords() are case-sensitive. Use lowercase or normalize terms for consistency:
->searchKeywords(['invoice', 'payment']),
dd(static::discoverClusterCards()) to inspect auto-discovered cards and debug visibility issues.filament-cards-*, so avoid overriding these in your theme.CardItems. Internal URLs (e.g., /path) are resolved via Filament’s router, while external URLs (e.g., https://) open in the same tab by default.discoverClusterCards() or discoverResourceCards() in a child class to filter or transform discovered cards:
protected static function getCards(): array {
return collect(static::discoverClusterCards())
->reject(fn ($card) => str_contains($card->getUrl(), 'hidden'))
->values()
->toArray();
}
CardGroup::make(fn () => auth()->user()->isAdmin() ? 'Admin Tools' : 'Tools')
->schema([...]),
php artisan vendor:publish --tag="filament-cards-config"
extraAttributes():
CardItem::make(DangerZone::class)
->badge('Critical')
->extraAttributes(['style' => 'background-color: #dc2626;']),
CardGroup::make()->collapsible()) to reduce initial load time.static::cache(fn () => static::discoverClusterCards(), 'cluster-cards-cache', now()->addHours(1));
$navigationDescription) over manually setting them on every CardItem to reduce boilerplate.$navigationIcon is set on the CardsPage for proper sidebar integration. Use Heroicons (e.g., 'heroicon-o-squares-2x2') for consistency.shouldRegisterNavigation(), canAccess()). Override these carefully to avoid breaking card visibility.CardItems, use absolute paths (e.g., /settings/company) or full URLs (e.g., https://example.com). Relative paths may fail to resolve.How can I help you explore Laravel packages today?