Solution Forest Web development agency based in Hong Kong. We help customers to solve their problems. We Love Open Soruces.
We have built a collection of best-in-class products:
- InspireCMS: A full-featured Laravel CMS with everything you need out of the box. Build smarter, ship faster with our complete content management solution.
- Filaletter: Filaletter - Filament Newsletter Plugin
- Website CMS Management: A hands-on Filament CMS plugin for those who prefer more manual control over their website content management.
This plugin provides a flexible tab layout system for Filament Admin panels, enabling you to organize content into clean, navigable tabbed interfaces.
Create simple tabs with individual Livewire components or build complex multi-content tabs containing HTML, strings, and multiple components. Features include customizable icons and badges, external link tabs, URL persistence, and full integration with Filament's widget system.
Demo site : https://filament-cms-website-demo.solutionforest.net/admin
Demo username : [email protected]
Demo password : 12345678 Auto Reset every hour.
Filament Version | Plugin Version |
---|---|
v2 | 1.x.x |
v3 | 2.x.x |
v4 | 3.x.x |
You can install the package via composer:
composer require solution-forest/tab-layout-plugin
Optionally, you can publish the views using
php artisan vendor:publish --tag="tab-layout-plugin-views"
Create tabbed interfaces with individual Livewire components using the TabsWidget::make()
method. This is the quickest way to get started with basic tab functionality.
First, register the TabsWidget
in your Filament panel provider:
<?php
namespace App\Providers\Filament;
use Filament\Panel;
use Filament\PanelProvider;
class AdminPanelProvider extends PanelProvider
{
public function panel(Panel $panel): Panel
{
return $panel
->widgets([
\SolutionForest\TabLayoutPlugin\Widgets\TabsWidget::class,
]);
}
}
// In App\Filament\Resources\Users\Pages\ListUsers.php
use SolutionForest\TabLayoutPlugin\Schemas\SimpleTabSchema;
use SolutionForest\TabLayoutPlugin\Widgets\TabsWidget;
class ListUsers extends ListRecords
{
protected function getHeaderWidgets(): array
{
return [
TabsWidget::make([
SimpleTabSchema::make(
label: 'Account Widget',
id: 'account_widget',
)->livewireComponent(\Filament\Widgets\AccountWidget::class),
SimpleTabSchema::make(
label: 'Edit User',
)->livewireComponent(\App\Filament\Resources\Users\Pages\EditUser::class, ['record' => 1]),
SimpleTabSchema::make('Link')
->url('https://example.com', true)
->icon('heroicon-o-globe-alt'),
]),
];
}
}
This approach provides three ways to configure tabs:
- TabWidgetContentConfiguration object - Most explicit and type-safe
- Array syntax - Simpler for basic configurations
- Chain tab() method - Useful for adding tabs conditionally
You can also create multiple Livewire components, HTML, and strings inside each tab. You can even make a tab act as a redirect link by extending the TabsWidget
class.
To generate a Tab widget:
php artisan make:filament-tab-widget DummyTabs
You will then define the child components in the schema()
method to display inside:
namespace App\Filament\Widgets;
use SolutionForest\TabLayoutPlugin\Components\Tabs\Tab as TabLayoutTab;
use SolutionForest\TabLayoutPlugin\Schemas\Components\LivewireContainer;
use SolutionForest\TabLayoutPlugin\Schemas\Components\TabContentContainer;
use SolutionForest\TabLayoutPlugin\Widgets\TabsWidget as BaseWidget;
class DummyTabs extends BaseWidget
{
protected function schema(): array
{
return [
TabLayoutTab::make('Label 1')
->icon('heroicon-o-bell')
->badge('39')
->schema([
// Display Livewire component
LivewireContainer::make(\Filament\Widgets\AccountWidget::class),
// Display HTML
str('
## This is dummy HTML code inside a tab
- This is a bullet point
- Another bullet point
```php
echo "This is a code block";
```')->markdown()->toHtmlString(),
]),
TabLayoutTab::make('Label 2')
->schema([
// Display raw string
'Raw string here',
// Display Livewire component
app(\App\Livewire\Dummy::class, ['__id' => uniqid() . '-dummy']),
// Display Livewire component with data
LivewireContainer::make(\App\Filament\Resources\Users\Pages\EditUser::class)
->data(['record' => 1]),
LivewireContainer::make(\Filament\Widgets\AccountWidget::class)
->columnSpan(1),
LivewireContainer::make(\Filament\Widgets\AccountWidget::class)
->columnSpan(1),
])
->columns(2),
// External link
TabLayoutTab::make('Go To FilamentPHP (Link)')
->url("https://filamentphp.com/", true),
];
}
}
Tabs may have an icon and badge, which you can set using the icon()
and badge()
methods:
TabLayoutTab::make('Label 1')
->icon('heroicon-o-bell')
->badge('39')
->schema([
// ...
]),
Additionally, you have the option to pass an array of data to your component.
protected function schema(): array
{
return [
TabLayoutTab::make('Label 1')
->icon('heroicon-o-bell')
->badge('39')
->schema([
LivewireContainer::make(\Filament\Widgets\AccountWidget::class),
// Display Livewire component with data
LivewireContainer::make(ViewProductCategory::class)
// The Data of target component
->data(['record' => 1]),
]),
TabLayoutTab::make('Label 2')
->schema([
LivewireContainer::make(\Filament\Widgets\FilamentInfoWidget::class),
]),
];
}
Then, add the tab widget to your page, e.g.:
// In App\Resources\UserResource\ListUsers.php
class ListUsers extends ListRecords
{
protected function getHeaderWidgets(): array
{
return [
\App\Filament\Widgets\DummyTabs::class,
];
}
}
Control which tab is active when the widget loads. You can set this either dynamically with a callback or with a static tab order.
use SolutionForest\TabLayoutPlugin\Components\Tabs;
use SolutionForest\TabLayoutPlugin\Widgets\TabsWidget as BaseWidget;
class DummyTabs extends BaseWidget
{
public static function tabs(Tabs $tabs): Tabs
{
return $tabs
// Dynamic: Use a callback to determine the active tab
->activeTab(function (self $livewire, Tabs $component): int {
return 2; // Second tab will be active
})
// Static: Set a specific tab as active by order
->activeTab(2); // Second tab will be active
}
}
Maintain the selected tab state when users reload the page or share URLs. This feature saves the active tab in the browser's query string, providing a better user experience.
Requirements:
- Each tab must have a unique
id
- The tab group needs an
id
attribute - Define a Livewire property to store the active tab state
use SolutionForest\TabLayoutPlugin\Components\Tabs;
use SolutionForest\TabLayoutPlugin\Components\Tabs\Tab as TabLayoutTab;
use SolutionForest\TabLayoutPlugin\Widgets\TabsWidget as BaseWidget;
class DummyTabs extends BaseWidget
{
// Property to store the active tab state
public $activeTab = '';
// Enable Livewire query string binding for URL persistence
public function queryString()
{
return ['activeTab'];
}
public static function tabs(Tabs $tabs): Tabs
{
return $tabs
->id('dummy-tabs') // Required: unique ID for the tab group
// Option 1: Use a callback to determine the query parameter name
->persistTabInQueryString(function ($component, $livewire) {
return 'activeTab'; // Property name to sync with URL
})
// Option 2: Direct property name for URL persistence
->persistTabInQueryString('activeTab');
}
protected function schema(): array
{
return [
TabLayoutTab::make(label: 'Tab 1', id: 'sample-1')
->schema([
// Tab 1 content...
]),
TabLayoutTab::make(label: 'Tab 2', id: 'sample-2')
->schema([
// Tab 2 content...
]),
];
}
}
Note: When using URL persistence, each tab must have a unique
id
and the tab group needs anid
attribute.
By default, tabs and their content are wrapped in a styled card container. You can remove this container styling using the contained()
method:
use SolutionForest\TabLayoutPlugin\Components\Tabs;
class DummyTabs extends BaseWidget
{
public static function tabs(Tabs $tabs): Tabs
{
return $tabs
->contained(false);
}
}
In addition to using the LivewireContainer
component, you can create your own custom tab layout components by extending the TabLayoutComponent
class or using the php artisan tab-layout:component
command.
For example, the following PHP code defines a FilamentInfoWidget class that extends TabLayoutComponent and specifies a ComponentTabComponent
as the tab component to use. The getData method can be used to populate the component with data.
<?php
namespace App\Filament\Tabs\Components;
use Filament\Widgets\FilamentInfoWidget as ComponentTabComponent;
use SolutionForest\TabLayoutPlugin\Components\Tabs\TabLayoutComponent;
class FilamentInfoWidget extends TabLayoutComponent
{
protected ?string $component = ComponentTabComponent::class;
public function getData(): array
{
return [
// Data to assign to component
];
}
}
You can also use the php artisan tab-layout:component
command to generate the code for a new tab layout component. For example, to generate a FilamentInfoWidget
component, you can run the following command:
php artisan tab-layout:component FilamentInfoWidget Filament\Widgets\FilamentInfoWidget
After creating your custom tab layout component by extending the TabLayoutComponent
class, you can register it on the schema of a TabLayoutTab
instance.
protected function schema(): array
{
return [
...
TabLayoutTab::make('Label 3')
->schema([
\App\Filament\Tabs\Components\FilamentInfoWidget::make()
// ->data([]), // Also can assign data here
]),
];
}
Please see CHANGELOG for more information on what has changed recently.
If you discover any security related issues, please email [email protected] instead of using the issue tracker.
The MIT License (MIT). Please see License File for more information.