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 Connection Badge Laravel Package

rawand201/filament-connection-badge

A drop-in connection status badge for Filament panels: a signal-bars icon in the topbar, a live ping graph on hover, and a full-screen overlay when the connection drops. Supports Filament v4 and v5, RTL, dark mode, permissions, and custom render hooks.

View on GitHub
Deep Wiki
Context7

Getting Started

Install — three commands, one of them non-negotiable.

composer require rawand201/filament-connection-badge
php artisan filament:assets                 # REQUIRED — do not skip
php artisan vendor:publish --tag="filament-connection-badge-config"

The README calls this "drop-in", and it mostly is — the service provider is auto-discovered and the badge appears in every panel's topbar with zero code. But the CSS/JS ship prebuilt in the package (resources/dist) and are registered through FilamentAsset. Until you run filament:assets, the render hook fires and you get an unstyled <span> that never pings. Re-run it in every deploy pipeline after composer install/update, right next to filament:upgrade if you use it.

Verify before you configure. Three checks, in this order:

Check Command / place What "good" looks like
Route registered php artisan route:list --name=filament-connection-badge GET /_filament-connection-badge/ping, middleware web, no throttle by default
Badge renders Any authenticated panel page, topbar next to avatar Signal-bars icon styled with theme colors, not a bare glyph
Pings fire DevTools → Network → filter _filament-connection-badge JSON { ok: true, ts: … } every 5s, Cache-Control: no-store

First use case: drop it into your internal admin panel and watch it for a week before touching the config. Hover the badge and confirm the tooltip's sparkline climbs, the average is plausible for your hosting region, and the host indicator matches the app. Then commit a regression test alongside the install so nobody "cleans up" the route later:

it('serves the connection badge heartbeat', function () {
    $this->get(route('filament-connection-badge.ping'))
        ->assertOk()
        ->assertJson(['ok' => true])
        ->assertHeader('Cache-Control', 'no-store, private');
});

Know what you're adopting. Six stars, zero dependents, one maintainer, MIT, readme+changelog maturity. That's fine for a UI nicety, but pin it exactly ("rawand201/filament-connection-badge": "1.0.1") rather than floating, and read the changelog on bump — 1.0.1 was security-only housekeeping, which tells you the maintainer is responsive but the surface area is still young.

Implementation Patterns

Harden the heartbeat in the install commit, not a follow-up ticket. The ping route is publicly reachable under web by default, and it's a free PHP execution hook. Do all three levers at once:

// config/filament-connection-badge.php
'enabled'    => env('FILAMENT_CONNECTION_BADGE_ENABLED', true),
'permission' => 'viewConnectionBadge',
'route' => [
    'prefix'     => '_filament-connection-badge',
    'middleware' => ['web', \Filament\Http\Middleware\Authenticate::class],
    'throttle'   => '120,1',
],
// AppServiceProvider::boot()
Gate::define('viewConnectionBadge', fn (User $u) => $u->hasAnyRole(['super_admin', 'support']));

The two mechanisms do different jobs: the auth middleware keeps unauthenticated traffic off PHP entirely, while the Gate hides the badge and returns 403 from the ping. You want both.

Use permission as your per-panel switch. enabled is one global boolean — it can't vary per panel or per tenant. The only runtime lever is the Gate ability, evaluated at render time via auth()->user()->can(). Pattern: internal admin panel → no permission (everyone sees it); customer-facing panel → Gate restricts to staff, or closes over filament()->getCurrentPanel()->getId() if the ability should only pass in one panel.

Use env vars as the deployment boundary. FILAMENT_CONNECTION_BADGE_ENABLED=false on staging/CI and local keeps the noise out of logs and stops the overlay from interrupting screenshots and e2e runs. Prefer env for THROTTLE, PERMISSION, and RENDER_HOOK; keep thresholds in config where they're visible and diffable.

Tune for your actual user base, not a LAN. Defaults are full ≤ 200ms, medium ≤ 600ms, else low, sampled every 5000ms with a 30-sample window. For VPN users, mobile field staff, or a panel hosted far from its users, 200ms is a permanently-yellow lie. Measure a real week of tooltip averages from a few regions and set thresholds from that data — e.g. full => 400, medium => 900.

Place it where it reads. Default panels::user-menu.before puts it beside the avatar, which is where users look for status. panels::topbar.end is the other sensible choice if your user menu is already crowded. Check Filament\View\PanelsRenderHook for valid values — a typo'd hook fails silently (nothing renders, no error).

Keep the built-in ping URL. ping_url exists as an escape hatch, and the README's own example points it at /favicon.ico — which makes the ping cheaper but throws away the package's best property: the timing reflects PHP + router + middleware, so a green badge genuinely means your app is answering, not nginx serving a static file. Only repoint it if you're deliberately trading fidelity for load.

Customize at the CSS layer, not the view layer. Every color is a Filament token (var(--success-*), var(--warning-*), var(--gray-*)), and every element has an .fcb-* class. Write a handful of overrides against those classes and you inherit dark mode and panel theme for free. Only vendor:publish --tag="filament-connection-badge-views" if you need structural changes — published views become yours to maintain across Filament 4→5.

Gotchas and Tips

It's a per-page-view request multiplier. no-store means every ping is a cold, uncached PHP request through the full middleware stack. A 5s interval is 12 req/min per browser tab — 40 active admins with two tabs each is ~960 req/min of pure overhead. Raise ping_interval (10–15s is plenty for human-perceived status) before you scale headcount, and size throttle above 60 / (ping_interval_seconds) × tabs so you never self-inflict 429s.

403 and 429 look like "offline" to the client. Any non-success ping counts as a failed sample and feeds packet loss, which can trip the full-screen overlay. Two consequences: with permission set, an unauthenticated or under-privileged visitor sees offline rather than hidden, and an over-tight throttle produces a convincing fake outage. If the badge can appear on a login or error page in your setup, set show_overlay => false there or scope the Gate carefully.

Session expiry may read as "online". If you add auth middleware, an expired session returns a 302 to the login page; fetch follows redirects and hands back a 200 HTML body, which a naive response.ok check treats as success. Don't use this badge as a substitute for a real "am I still logged in" indicator — verify the behaviour in your own build before relying on it.

Livewire SPA navigation is worth testing. The Alpine component keeps an interval and online/offline listeners. Navigate between wire:navigate pages and panels, then watch the Network tab: you want one steady 5s cadence, not two or three stacked timers. If you see multiplying requests, that's the first thing to file upstream or patch in a published view.

The overlay is modal and full-screen. It auto-dismisses on recovery, but on genuinely flaky connections (tunnels, elevators, mobile handoffs) it will interrupt form entry. For field-facing panels, show_overlay => false and let the badge alone carry the signal.

Thresholds are strict inequalities you can't see. Low latency wins until it crosses full, then medium, then everything above is low. There's no hysteresis in the config, so a connection sitting right on a threshold will flap between colors. If that's annoying, shift your thresholds a few ms past typical values rather than trying to smooth it in CSS.

RTL work is done — don't undo it. Layout uses CSS logical properties and the ping graph keeps LTR via unicode-bidi: isolate. When you write .fcb-* overrides, avoid margin-left/float and don't touch unicode-bidi/direction, or you'll break Arabic and Kurdish Sorani (en, ar, ckb ship in the box; publish translations to add more).

Debugging cheatsheet: DevTools → Network, filter _filament-connection-badge. 200 + JSON = healthy. 403 = Gate blocking. 429 = throttle too tight. 302/HTML = auth redirect. No requests at all = assets not published, or enabled false, or render_hook typo. No badge but requests present = CSS override or a panel that doesn't render the configured hook.

Extension points, in order of safety: CSS overrides on .fcb-* → config/env → published config → published views → published assets. composer sync-assets in the README is a contributor command for the package repo (mirrors resources/js|css into resources/dist); it is not something you run in your app.

Keep it in perspective. This is client-side, per-browser, and entirely unpersisted — no samples are stored server-side. It tells you what one user's connection feels like right now, which is genuinely useful in support calls ("is it us or them?"). It is not an uptime monitor, and it will happily show green in one tab while another user is timing out.

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.
socialdept/atp-orm
cboxdk/laravel-assistant-client
socialdept/atp-resolver
socialdept/tenant-domains
tautvydasr/blade-class-names
socialdept/color-scales
maherelgamil/controller
coleus/widgets
coleus/table
coleus/users
coleus/music
coleus/health
coleus/calendar
coleus/notes
coleus/support
coleus/settings
coleus/apps
seampact/laravel
emmyconceptng/laravel-ng-postcode
jjsquady/round-robin