Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Filament Expiration Notice Laravel Package

marcelweidum/filament-expiration-notice

Filament plugin that adds configurable expiration notices to your Laravel admin panels, alerting admins before records expire or after they lapse. Display expiry-aware banners and badges on resources and tables with minimal setup.

View on GitHub
Deep Wiki
Context7

Getting Started

1. Install the package

composer require marcelweidum/filament-expiration-notice

2. Publish what you need

Most Filament plugins ship publishable config/views/translations. Publish the config first, then only publish views if you intend to restyle:

php artisan vendor:publish --tag="filament-expiration-notice-config"
# optional, only if you need to override markup:
php artisan vendor:publish --tag="filament-expiration-notice-views"
php artisan vendor:publish --tag="filament-expiration-notice-translations"

3. Register the plugin on your panel

This is a panel-level plugin, so it belongs in your PanelProvider, not in AppServiceProvider:

use MarcelWeidum\FilamentExpirationNotice\FilamentExpirationNoticePlugin;

public function panel(Panel $panel): Panel
{
    return $panel
        ->default()
        ->id('admin')
        ->path('admin')
        // ...
        ->plugins([
            FilamentExpirationNoticePlugin::make(),
        ]);
}

4. Where to look first

  • config/filament-expiration-notice.php (once published) — this is where the enable flag, title/body copy, button label, and redirect behaviour typically live.
  • The package's resources/views directory — the modal is a Blade view, so anything not exposed in config is overridable there.
  • Your own config/session.php — the modal only ever appears because SESSION_LIFETIME elapsed, so tune that first if the notice feels "too aggressive" in testing.

First use case: you have a Filament admin where users leave a tab open over lunch, come back, click "Save", and get Livewire's raw grey "Page Expired" overlay (or a bare 419 page). Registering the plugin replaces that with a Filament-styled modal that tells the user what happened and gives them a single action button to get back to work.

Implementation Patterns

Default registration (most apps stop here)

->plugins([
    FilamentExpirationNoticePlugin::make(),
])

Use this for the whole panel and move on. It is a UX-only plugin; there is no data model, no migration, no API surface to design around.

Configure the copy centrally

Session-expiry copy is one of those strings that product/legal actually cares about (data loss warnings, "your unsaved changes are gone"). Keep it in config or translations rather than hardcoding, so a copy change is not a deploy:

// config/filament-expiration-notice.php (names vary by version — read the published file)
return [
    'enabled' => true,
    'title'   => 'Your session has expired',
    'body'    => 'For security, we signed you out after a period of inactivity.',
    'button'  => 'Sign in again',
];

If your app is multilingual, prefer the translation route: override the package's lang file per locale in lang/vendor/filament-expiration-notice/{locale}/... and keep config to booleans/URLs.

Pair it with an intentional redirect

A reload of an expired page usually bounces the user to the login screen anyway — but only after a second failed request. Point the modal's action at the login route (or a ->url()) so the flow is one click, not two:

FilamentExpirationNoticePlugin::make()
    ->redirectTo(route('filament.admin.auth.login'))

The exact fluent method depends on the installed version; if it isn't there, the view override is the fallback (see Gotchas).

Per-panel behaviour

Because it's a plugin, panels are independent. A customer-facing panel might want a soft "You were away, please refresh" message with a reload button, while the internal admin panel wants "Session expired — sign in again" pointing at the logout route. Register it twice with different config, or use ->plugin() conditionally:

->plugins(array_filter([
    $panel->getId() === 'admin' ? FilamentExpirationNoticePlugin::make() : null,
]))

Testing session expiry locally without waiting an hour

You cannot reasonably wait out SESSION_LIFETIME (default 120 minutes). Fast loop:

  1. Set SESSION_LIFETIME=1 in .env (minutes) and php artisan config:clear.
  2. Open the panel, log in, leave the tab.
  3. After ~1–2 minutes, click any Livewire action (a table sort, a Save button).
  4. The Livewire request 419s and the modal should render.

If you want to see it instantly, clear the session cookie from devtools before triggering an action — same code path, no waiting.

Verify you didn't break the normal 419 path

Keep a manual test that hits a route with an invalid CSRF token outside Livewire. The plugin only replaces the Livewire page-expired UX; your resources/views/errors/419.blade.php should still exist and look sane for full-page requests.

Gotchas and Tips

You probably already have Livewire's default modal — make sure you're replacing, not stacking. Livewire ships its own "Page Expired" dialog. If both render, users see two overlays or a half-styled one behind the other. After registering the plugin, confirm only one modal appears; if two do, check for a stale published Livewire asset or a custom JS listener on the page-expired browser event.

SESSION_LIFETIME is in minutes, session.expire_on_close is separate. A surprising number of "the plugin doesn't fire" reports are really "the user's browser is restoring the tab from bfcache". When a tab is restored, Livewire may not make a request at all until the user interacts — which is correct behaviour, but it means the modal appears on the next click, not on return. Set expectations with stakeholders accordingly.

Long-running workers and Octane. If you run Octane or a queue-heavy setup, remember session expiry is driven by the cookie + server-side store, not by PHP process lifetime. Nothing in this plugin changes that, but don't "fix" expiry issues by touching Octane config — check config/session.php and your cookie domain (a mismatch between SESSION_DOMAIN and the panel host makes every request look expired, and the modal will fire constantly).

Multiple tabs = logout storm. Logging in on tab B regenerates the session ID; tab A's next Livewire request fails. The modal will show on tab A and its button will take the user to login — where they're already logged in on tab B. Consider wording the button as "Reload" rather than "Sign in again" in apps where users commonly work in multiple tabs.

Don't rely on it for actual auth protection. This is presentation. Authorisation still lives in policies, auth middleware, and Filament::auth(). A pretty modal does not prevent a stale page from showing cached data — if you render anything sensitive, ensure it's re-fetched server-side on interaction (Filament resources do this by default; custom widgets that cache in Alpine state do not).

Cache the config after every change. php artisan config:cache is standard, but it's the #1 reason "I changed the title and nothing happened" in production. Same for translations if you run php artisan optimize.

Extension points, in order of preference:

  1. Config — booleans, strings, redirect URLs. Cheapest, upgrade-safe.
  2. Translations — per-locale copy, keeps config clean.
  3. Published views — for markup/branding changes. Note that published views go stale on major upgrades; diff them against the package's resources/views after every minor bump.
  4. Your own Livewire listener — if you need to log the event (e.g. Log::info('session expired', ['user' => auth()->id()])) or fire analytics. Listen for Livewire's page-expired browser event in a small JS snippet in your panel's renderHook, rather than forking the package.

Upgrade hygiene. At 95 stars and a small surface area, this is a "read the diff" dependency, not a "trust the semver" one. Pin it ("marcelweidum/filament-expiration-notice": "^1.0") and check the release notes against your Filament major version — Filament v3→v4 style bumps are where panel plugins break, typically on the plugin contract or the modal component namespace.

If the modal never appears at all, work down this list: is the plugin registered on this panel? is enabled true in the cached config? does your custom panel layout still include Filament's @livewireScripts/asset stack (removing it kills Livewire's error handling entirely)? and finally, is the failure actually a 419, or a 403/500 that just looks similar in the console?

Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
3mdikram/worldmoneywords
snowsoft/config
itmosoft/laravel-subscriptions
larva/laravel-ai-qwen
diephp/sequences
users-au/socialite-provider
twidpay/twid-logger
mrnewport/laravel-plaid
mrnewport/laravel-repo
mrnewport/laravel-docsign
mrnewport/laravel-stow
mrnewport/laravel-flow
mrnewport/laravel-priceable
mrnewport/laravel-nestedterms
fredmeng/laravel-apidoc-generator
itstructure/laravel-multilingual-tools
pdustdar/elasticsearch
markupai/php-sdk
rlacerda83/lumem-microservice-shipment
uni-method/symfony-lib