harvirsidhu/filament-cards
Composer 安装命令:
composer require harvirsidhu/filament-cards
包简介
A Filament-native cards plugin for organizing pages and resources into a card-based navigation hub.
关键字:
README 文档
README
Turn any Filament page into a card-based navigation hub — perfect for Settings hubs, Cluster front pages, Resource dashboards, or any place you want a clean grid of links instead of a sidebar tree.
It feels like part of Filament: same API patterns (label, schema, columnSpan, visible/hidden), respects your existing navigation config, and auto-discovers Cluster/Resource pages with full authorization checks.
Table of Contents
- Why use this?
- Which approach fits your case?
- Requirements
- Installation
- 60-Second Quick Start
- Use Case A — Cluster Front Page (most common)
- Use Case B — Resource Hub
- Use Case C — Standalone Settings Page
- API Reference —
CardItem - API Reference —
CardGroup - API Reference —
CardsPageConfiguration - API Reference — Page/Resource Hooks (for auto-discovery)
- Advanced — Dynamic Registration
- Advanced — Custom Discovery Filtering
- Full Example
- Optional — Plugin Registration
- License
Why use this?
| Feature | What it gives you |
|---|---|
| Auto-discovery | Reads pages/resources in a Cluster (or Resource) and creates cards automatically — no manual list to maintain. |
| Filament-native API | label, description, schema, visible, hidden, columnSpan — same patterns you already use. |
| Authorization-aware | Calls canAccess() on each component before rendering. |
| Grouping & layout | CardGroup, columns, spans, compact mode, collapsible sections. |
| Flexible visibility | Per-card or per-page hooks; central exclude lists; closure-based conditions. |
| Manual + discovered | Mix auto-discovered cards with hand-crafted ones (e.g., external links). |
| Dynamic registration | Add cards from service providers — useful for modular apps and packages. |
| Client-side search | Optional live search that filters by label, description, badge, and custom keywords. |
Which approach fits your case?
| Your situation | Use this |
|---|---|
| You have a Cluster with several pages and want a landing page | Cluster Front Page |
| You have a Resource with many custom pages | Resource Hub |
| You just want a generic settings/control panel with manual cards | Standalone Page |
| You want to add cards from a package or module | Dynamic Registration |
Requirements
- PHP 8.2+
- Laravel 11+
- Filament v4 or v5
Installation
Install the package via Composer:
composer require harvirsidhu/filament-cards
Theme setup (required for styling)
The plugin uses Tailwind classes, so add its views to your Filament theme so Tailwind picks them up.
In your theme.css, add:
@source '../../../../vendor/harvirsidhu/filament-cards/resources/views';
Then rebuild assets:
npm run build
60-Second Quick Start
The smallest possible cards page:
namespace App\Filament\Pages; use Harvirsidhu\FilamentCards\CardItem; use Harvirsidhu\FilamentCards\Filament\Pages\CardsPage; 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), ]; } }
That's it — Filament will pick up your new page, and clicking each card navigates to the linked page.
Use Case A — Cluster Front Page (most common)
The most powerful pattern: a CardsPage that automatically lists every page and resource in its Cluster.
Step 1 — Define the Cluster
namespace App\Filament\Clusters; use Filament\Clusters\Cluster; class Settings extends Cluster { protected static ?string $navigationIcon = 'heroicon-o-cog-8-tooth'; }
Step 2 — Create the front-page CardsPage
namespace App\Filament\Clusters\Settings\Pages; use App\Filament\Clusters\Settings; use Harvirsidhu\FilamentCards\Filament\Pages\CardsPage; class SettingsHub extends CardsPage { protected static ?string $cluster = Settings::class; protected static ?int $navigationSort = -1; // Show first in the cluster protected static function getCards(): array { return static::discoverClusterCards(); } }
Step 3 — Your other Cluster pages need no changes
Your existing pages just work. To add a description on the card, add a single property:
class CompanySettings extends Page { protected static ?string $cluster = Settings::class; protected static ?string $navigationIcon = 'heroicon-o-building-office'; public static ?string $navigationDescription = 'Manage company name, address, and branding.'; }
Breadcrumbs (Dashboard > Settings > Company Settings) work automatically.
What discoverClusterCards() does
In order, for every component registered to the Cluster:
- Skips the CardsPage itself.
- Calls
canAccess()(respects authorization). - Checks
showInFilamentCards()/$showInFilamentCards— falls back toshouldRegisterNavigation(). - Reads
$navigationLabel,$navigationIcon, and the resolved URL. - Reads
getNavigationBadge()/getNavigationBadgeColor()if defined. - Reads
$navigationDescription(orgetNavigationDescription()) if defined. - Groups by
getFilamentCardsGroup()/$filamentCardsGroup— falls back to$navigationGroup. - Sorts each group by
$navigationSort.
Hiding a card from auto-discovery
Option 1 — on the Page/Resource (recommended):
class InternalToolsPage extends Page { public static function showInFilamentCards(): bool { return false; } } // or as a property class AuditLogsResource extends Resource { public static bool $showInFilamentCards = false; }
To force-show a page that's hidden from the sidebar:
class HiddenFromSidebarPage extends Page { public static function shouldRegisterNavigation(): bool { return false; } public static function showInFilamentCards(): bool { return true; } }
Option 2 — central exclude list on the CardsPage:
class SettingsHub extends CardsPage { protected static array $excludedClusterComponents = [ AuditLogsResource::class, InternalToolsPage::class, ]; }
Custom group name (different from the sidebar)
class CompanySettings extends Page { public static function getFilamentCardsGroup(): ?string { return 'Business Settings'; } } // or property form class BillingResource extends Resource { public static ?string $filamentCardsGroup = 'Finance'; }
Mixing discovered cards with manual ones
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(), ]), ]; }
Use Case B — Resource Hub
When a Resource has many custom pages, use discoverResourceCards() to auto-create a card for each:
namespace App\Filament\Resources\UserResource\Pages; use App\Filament\Resources\UserResource; use Harvirsidhu\FilamentCards\Filament\Pages\CardsPage; class UserSettingsHub extends CardsPage { protected static string $resource = UserResource::class; protected static function getCards(): array { return static::discoverResourceCards(); } }
Same hooks (showInFilamentCards, $navigationDescription, etc.) work here. To exclude specific pages:
protected static array $excludedResourcePages = [ UserResource\Pages\DangerZone::class, ];
Use Case C — Standalone Settings Page
No Cluster, no Resource — just a manually-curated cards page:
use Harvirsidhu\FilamentCards\CardGroup; use Harvirsidhu\FilamentCards\CardItem; use Harvirsidhu\FilamentCards\Filament\Pages\CardsPage; class ControlPanel extends CardsPage { protected static ?string $navigationIcon = 'heroicon-o-squares-2x2'; protected static function getCards(): array { return [ CardGroup::make('General') ->icon('heroicon-o-cog') ->description('Core settings') ->schema([ CardItem::make(CompanySettings::class)->color('primary'), CardItem::make(BillingSettings::class)->color('success'), ]), CardItem::make('/external/docs') ->label('Documentation') ->icon('heroicon-o-document-text') ->openUrlInNewTab(), ]; } }
API Reference — CardItem
CardItem represents a single clickable card. Pass a Filament Page class, Resource class, or a URL string:
CardItem::make(CompanySettings::class) // Filament Page CardItem::make(UserResource::class) // Filament Resource CardItem::make('/custom/path') // Internal URL CardItem::make('https://example.com') // External URL
When given a Page/Resource class, the card auto-resolves label, icon, badge, badgeColor, and url from the class's navigation properties.
Method overview
| Method | Purpose |
|---|---|
label() |
Override the card title |
description() |
Subtitle below the title |
badge() |
Right-aligned badge |
badgeColor() |
Badge color |
icon() |
Override the card icon |
url() / openUrlInNewTab() |
Override target URL |
alignment() |
Text alignment inside the card |
color() |
Color accent on the card |
visible() / hidden() |
Conditional rendering |
disabled() |
Render but make non-clickable |
sort() |
Order within a group |
columnSpan() / columnSpanFull() |
Grid span |
searchKeywords() |
Extra terms for the search bar |
extraAttributes() |
Custom HTML attributes |
label()
Override the card title. Accepts a string or Closure:
CardItem::make(CompanySettings::class) ->label('Company') CardItem::make(CompanySettings::class) ->label(fn () => __('settings.company'))
description()
Add a subtitle below the title:
CardItem::make(CompanySettings::class) ->description('Manage company name, address, and branding')
Auto-discovery shortcut: add
public static ?string $navigationDescriptionto the page/resource anddiscoverClusterCards()/discoverResourceCards()will pick it up automatically.
badge() / badgeColor()
CardItem::make(CompanySettings::class) ->badge('Beta') ->badgeColor('primary')
Auto-discovery: read from
getNavigationBadge()/getNavigationBadgeColor()when present on the discovered Page/Resource.
icon()
CardItem::make('/path') ->icon('heroicon-o-building-office')
url() / openUrlInNewTab()
CardItem::make(CompanySettings::class) ->url('https://custom-url.com') ->openUrlInNewTab()
alignment()
Text alignment inside the card. Options: Start, Center, End, Justify.
use Filament\Support\Enums\Alignment; CardItem::make(CompanySettings::class) ->alignment(Alignment::Center)
color()
Color accent on the card. Available: primary, success, danger, warning, info, gray.
CardItem::make(BillingSettings::class)->color('success') CardItem::make(DangerZone::class)->color('danger')
visible() / hidden()
Boolean or Closure:
CardItem::make(BillingSettings::class) ->visible(fn () => auth()->user()->can('manage-billing')) CardItem::make(DangerZone::class) ->hidden(fn () => ! auth()->user()->isAdmin())
disabled()
Render the card but make it non-clickable with reduced opacity:
CardItem::make(DangerZone::class) ->disabled(fn () => ! auth()->user()->isAdmin())
sort()
Order cards within a group:
CardItem::make(CompanySettings::class)->sort(1) CardItem::make(BillingSettings::class)->sort(2)
columnSpan() / columnSpanFull()
CardItem::make(CompanySettings::class)->columnSpan(2) CardItem::make(NotificationPrefs::class)->columnSpanFull() // Responsive CardItem::make(CompanySettings::class)->columnSpan([ 'default' => 1, 'md' => 2, 'lg' => 3, ])
searchKeywords()
Extra terms used by the page's search bar (when $searchable = true on the CardsPage). Useful for aliases and jargon — the keywords are not displayed.
CardItem::make(BillingSettings::class) ->searchKeywords(['invoices', 'payments', 'subscriptions', 'finance']) CardItem::make(CompanySettings::class) ->searchKeywords('organisation') // single string ok CardItem::make(LegacyTools::class) ->searchKeywords(fn () => $this->resolveLegacyAliases())
Auto-discovery: declare on the page/resource directly:
public static array $filamentCardsSearchKeywords = ['organisation', 'org']; // …or as a method public static function getFilamentCardsSearchKeywords(): array { return ['organisation', 'org']; }
extraAttributes()
Custom HTML attributes on the card element:
CardItem::make(CompanySettings::class) ->extraAttributes([ 'data-analytics' => 'company-settings', 'id' => 'company-card', ])
API Reference — CardGroup
Groups organize cards under a (collapsible) header — like Filament's Section.
Method overview
| Method | Purpose |
|---|---|
schema() |
Cards inside the group |
label() / description() / icon() |
Header customization |
columns() |
Override grid columns for this group |
collapsible() / collapsed() |
Collapse behavior |
compact() |
Tighter padding/gaps |
visible() / hidden() |
Hide the whole group |
schema()
CardGroup::make('General') ->schema([ CardItem::make(CompanySettings::class), CardItem::make(BillingSettings::class), ])
label() / description() / icon() (group)
CardGroup::make('General') ->label('General Settings') ->description('Core application configuration') ->icon('heroicon-o-cog') ->schema([...])
columns()
Integer or responsive array (Filament widget style):
CardGroup::make('Wide Cards') ->columns(2) ->schema([...]) CardGroup::make('Wide Cards') ->columns([ 'md' => 2, 'xl' => 4, ]) ->schema([...])
collapsible() / collapsed()
CardGroup::make('Advanced')->collapsible()->schema([...]) // Starts collapsed (implicitly collapsible) CardGroup::make('Advanced')->collapsed()->schema([...]) CardGroup::make('Advanced') ->collapsed(fn () => ! auth()->user()->isAdmin()) ->schema([...])
compact()
CardGroup::make('Quick Links') ->compact() ->schema([...])
Group-level visible() / hidden()
CardGroup::make('Admin Only') ->hidden(fn () => ! auth()->user()->isAdmin()) ->schema([...])
API Reference — CardsPage Configuration
Configure the whole page with static properties.
| Property | Type | Default | Purpose |
|---|---|---|---|
$columns |
int|string|array |
3 |
Grid columns (responsive supported) |
$itemsAlignment |
Alignment |
Center |
Alignment of card content |
$iconSize |
IconSize |
Medium |
Card icon size |
$iconInlined |
bool |
false |
Inline icon with title (vs stacked) |
$iconPosition |
IconPosition |
Before |
Icon before or after the label |
$searchable |
bool |
false |
Show a client-side search bar |
$searchPlaceholder |
?string |
null ('Search…') |
Placeholder text for the search input |
$excludedClusterComponents |
array |
[] |
Skip these classes in discoverClusterCards() |
$excludedResourcePages |
array |
[] |
Skip these classes in discoverResourceCards() |
$columns
protected static string|int|array $columns = 4; // Responsive protected static string|int|array $columns = [ 'md' => 2, 'xl' => 4, ];
$itemsAlignment
use Filament\Support\Enums\Alignment; protected static Alignment $itemsAlignment = Alignment::Center;
$iconSize
use Filament\Support\Enums\IconSize; protected static IconSize $iconSize = IconSize::Small;
$iconInlined
protected static bool $iconInlined = true;
$iconPosition
use Filament\Support\Enums\IconPosition; protected static IconPosition $iconPosition = IconPosition::After;
$searchable
Renders a search bar at the top right. Filters cards live by label, description, badge, and any searchKeywords(). Empty groups are auto-hidden. Filtering happens client-side via Alpine.js — no server round-trips.
protected static bool $searchable = true;
$searchPlaceholder
protected static bool $searchable = true; protected static ?string $searchPlaceholder = 'Find a tool...';
$excludedClusterComponents
protected static array $excludedClusterComponents = [ AuditLogsResource::class, InternalToolsPage::class, ];
$excludedResourcePages
protected static array $excludedResourcePages = [ UserResource\Pages\DangerZone::class, ];
API Reference — Page/Resource Hooks (for auto-discovery)
These are read off your existing Pages and Resources by discoverClusterCards() / discoverResourceCards(). You add them only to pages you want discovered.
| Hook | Form | Purpose |
|---|---|---|
$navigationDescription |
public static ?string |
Subtitle text on the card |
getNavigationDescription() |
public static function (): ?string |
Same as above (method form) |
$showInFilamentCards |
public static bool |
Whether to include in cards |
showInFilamentCards() |
public static function (): bool |
Same (method form) |
$filamentCardsGroup |
public static ?string |
Override group name (otherwise $navigationGroup) |
getFilamentCardsGroup() |
public static function (): ?string |
Same (method form) |
$filamentCardsSearchKeywords |
public static array |
Extra search terms |
getFilamentCardsSearchKeywords() |
public static function (): array |
Same (method form) |
getNavigationBadge() |
Filament built-in | Read by discovery for the card badge |
getNavigationBadgeColor() |
Filament built-in | Read by discovery for the badge color |
$navigationLabel / $navigationIcon / $navigationSort / $navigationGroup |
Filament built-in | Used as defaults for label/icon/order/group |
Advanced — Dynamic Registration
Add cards from outside the class — useful for modular apps and packages. Call from a service provider's boot():
use App\Filament\Pages\ControlPanel; use Harvirsidhu\FilamentCards\CardItem; ControlPanel::addCards([ CardItem::make(UserManagement::class) ->label('User Accounts') ->icon('heroicon-o-users') ->description('Manage roles, permissions, and user accounts'), ]);
Advanced — Custom Discovery Filtering
Override shouldIncludeDiscoveredCard() for centralized inclusion logic that goes beyond exclude lists:
protected static function shouldIncludeDiscoveredCard(string $component): bool { if (! parent::shouldIncludeDiscoveredCard($component)) { return false; } return $component !== BetaFeaturePage::class; }
Full Example
Everything together — auto-discovery, manual cards, groups, conditional visibility, and an external link:
use Filament\Support\Enums\Alignment; use Filament\Support\Enums\IconPosition; use Filament\Support\Enums\IconSize; use Harvirsidhu\FilamentCards\CardGroup; use Harvirsidhu\FilamentCards\CardItem; use Harvirsidhu\FilamentCards\Filament\Pages\CardsPage; class SettingsHub extends CardsPage { protected static ?string $navigationIcon = 'heroicon-o-cog-8-tooth'; protected static int $columns = 3; protected static Alignment $itemsAlignment = Alignment::Center; protected static IconSize $iconSize = IconSize::Medium; protected static IconPosition $iconPosition = IconPosition::Before; protected static function getCards(): array { return [ CardGroup::make('General') ->icon('heroicon-o-cog') ->description('Core application settings') ->collapsible() ->schema([ CardItem::make(CompanySettings::class) ->color('primary'), CardItem::make(BillingSettings::class) ->visible(fn () => auth()->user()->can('manage-billing')) ->color('success') ->sort(2), CardItem::make(NotificationPrefs::class) ->description('Email, SMS & push notification preferences') ->columnSpanFull(), ]), CardGroup::make('Danger Zone') ->icon('heroicon-o-exclamation-triangle') ->collapsed() ->columns(2) ->schema([ CardItem::make(DangerZone::class) ->color('danger') ->disabled(fn () => ! auth()->user()->isAdmin()), ]), CardItem::make('https://docs.example.com') ->label('Documentation') ->icon('heroicon-o-book-open') ->openUrlInNewTab() ->extraAttributes(['data-track' => 'docs']), ]; } }
Optional — Plugin Registration
Not required, but you can register the plugin in your panel provider for clarity:
use Harvirsidhu\FilamentCards\FilamentCardsPlugin; public function panel(Panel $panel): Panel { return $panel ->plugins([ FilamentCardsPlugin::make(), ]); }
License
MIT — see License File.
harvirsidhu/filament-cards 适用场景与选型建议
harvirsidhu/filament-cards 是一款 基于 PHP 开发的 Composer 扩展包,目前已累计 7.28k 次下载、GitHub Stars 达 11, 最近一次更新时间为 2026 年 02 月 18 日, 在 PHP 生态内属于活跃度较高的组件。
它主要适用于以下技术方向: 「laravel」 「Settings」 「cluster」 「cards」 「control-panel」 「filament」 等业务场景。在实际项目中,围绕这些方向常见需要落地的问题包括:接口对接、性能调优、并发安全、与既有框架(Laravel / ThinkPHP / Yii / Webman 等)的兼容适配,以及生产环境的日志埋点与稳定性保障。
我们在过去多个企业项目中使用过 harvirsidhu/filament-cards 或与其功能相近的方案,如果你在选型或落地过程中遇到问题,例如 版本兼容、二次改造、私有化封装、与内部系统对接、生产 BUG 排查,欢迎联系我们协助评估。
基于 harvirsidhu/filament-cards 在你已有业务上做功能扩展、字段裁剪、UI 适配、与内部账号 / 权限 / 日志系统的深度对接。
线上偶发问题、内存泄漏、慢查询、并发异常等排查修复;针对高流量场景做缓存、队列、索引层面的调优。
承接完整的项目从需求 → 设计 → 开发 → 上线 → 长期运维;也可按月提供技术保姆服务。
与 harvirsidhu/filament-cards 相关的其它包
同方向 / 同关键字的高下载量 PHP Composer 包推荐,方便对比选型:
Persistent settings package for Laravel framework.
A Zend Framework module to quickly and easily set PHP settings.
Settings Card for Laravel Nova.
PHP Long Running Process Manager Cluster
Extends basic Wordpress features.
NeoClient is the most advanced Http Client for Neo4j
统计信息
- 总下载量: 7.28k
- 月度下载量: 0
- 日度下载量: 0
- 收藏数: 11
- 点击次数: 18
- 依赖项目数: 0
- 推荐数: 0
其他信息
- 授权协议: MIT
- 更新时间: 2026-02-18
