wezlo/filament-workspace-tabs
Browser-like tabs for Filament panels: open every page in a new tab, persist via localStorage, drag to reorder, pin tabs, reopen recently closed, and use right-click actions plus keyboard shortcuts. Works with Filament v4/v5, SPA and dark mode.
composer require wezlo/filament-workspace-tabs
PanelProvider:
public function panel(Panel $panel): Panel
{
return $panel->plugins([
WorkspaceTabsPlugin::make(),
]);
}
resources/css/filament/{panel}/theme.css):
@source '../../../../vendor/wezlo/filament-workspace-tabs/resources/views/**/*';
npm run build
Ctrl+click a link to open in a new tab without navigating away.Ctrl+W/Cmd+W to close the active tab.A developer working on a Filament admin panel with multiple pages (e.g., Resources, Pages, Settings) can now:
Dashboard tab to always keep it accessible.Tab Management:
Ctrl+click: Open links in new tabs without navigating away.x button on the tab.Ctrl+W/Cmd+W keyboard shortcut.Close, Close Others, Close to the Right, Close All).Navigation:
Livewire.navigate() for SPA transitions).Persistence:
localStorage under a panel-scoped key (default: filament_workspace_tabs_{panel_id}).Recently Closed Tabs:
Customizing Tab Labels:
Override the default tab label (extracted from document.title) by publishing the config and extending the TabLabel class:
php artisan vendor:publish --tag="filament-workspace-tabs-config"
Then modify config/filament-workspace-tabs.php or create a custom tab label resolver.
Excluding URLs: Prevent specific URLs (e.g., login page) from being tracked as tabs:
WorkspaceTabsPlugin::make()
->excludeUrls(['/admin/login', '/admin/register'])
Disabling Features: Disable drag-to-reorder or context menu for a cleaner UI:
WorkspaceTabsPlugin::make()
->dragReorder(false)
->contextMenu(false)
SPA Mode:
Works seamlessly with Filament’s ->spa() mode and Livewire’s wire:navigate. No additional setup required.
Dark Mode: Fully compatible with Filament’s dark mode. No manual adjustments needed.
Dynamic Tab Updates:
Use the tab-updated Alpine.js event to listen for tab changes (e.g., update UI based on active tab):
window.Alpine.store('tabs', {
init() {
this.$wire.on('tab-updated', (tab) => {
console.log('Active tab changed:', tab);
});
},
});
Custom Tab Actions:
Extend the context menu by publishing the config and overriding the ContextMenu component:
// config/filament-workspace-tabs.php
'context_menu_items' => [
'custom-action' => [
'label' => 'Custom Action',
'icon' => 'heroicon-o-star',
'action' => 'customAction',
],
];
Programmatic Tab Control: Use Livewire to manage tabs programmatically (e.g., close all tabs on logout):
public function logout()
{
$this->dispatch('close-all-tabs');
auth()->logout();
}
Cross-Panel Tab Bleed:
persistKey is unique per panel to avoid shared tab states.persistKey in the plugin config:
WorkspaceTabsPlugin::make()
->persistKey('panel_1_tabs') // Unique per panel
LocalStorage Conflicts:
persistKey, tab states may conflict.persistKey for each panel or application.SPA Navigation Issues:
Livewire.navigate() correctly, causing tab URLs to desync.->spa() and Livewire’s wire:navigate is properly set up.Tab Label Overrides Not Reflecting:
TabLabel resolver) aren’t updating, clear the browser’s localStorage or check for JavaScript errors.Drag-and-Drop Performance:
maxTabs limit or optimize the SortableJS configuration.Recently Closed Tabs Limit:
recentlyClosedTabsLimit in the config (if exposed) or by overriding the RecentlyClosedTabs component.Inspect Tab State:
F12) and check localStorage for the panel-scoped key (e.g., filament_workspace_tabs_{panel_id}) to debug tab persistence issues.Disable Features for Testing:
WorkspaceTabsPlugin::make()
->dragReorder(false)
->contextMenu(false)
Check for JavaScript Errors:
@source is correctly added to theme.css).Verify Plugin Registration:
panel() method of your PanelProvider).Clear Cache:
php artisan filament:cache-clear
Custom Tab Components:
Tab or TabBar views to customize styling or behavior:
php artisan vendor:publish --tag="filament-workspace-tabs-views"
resources/views/vendor/filament-workspace-tabs/.Custom Persistence Backend:
TabPersistence class to use a database or session storage instead of localStorage:
// config/filament-workspace-tabs.php
'persistence' => \App\Services\CustomTabPersistence::class,
Add Tab Metadata:
Tab model or using Alpine.js stores:
document.addEventListener('alpine:init', () => {
Alpine.store('customTabData', {
init() {
this.data = {};
},
set(tabId, key, value) {
this.data[tabId] = this.data[tabId] || {};
this.data[tabId][key] = value;
},
});
});
Tab Events:
tab-opened, `tab-closedHow can I help you explore Laravel packages today?