matheusmarnt/ghostwire
| Install | |
|---|---|
composer require matheusmarnt/ghostwire |
|
| Latest Version: | v1.3.0 |
| PHP: | ^8.2 |
| License: | MIT |
| Last Updated: | Sep 22, 2026 |
| Links: | GitHub · Packagist |
Ghostwire
Automatic runtime skeleton loaders for Livewire — synthesized from your live DOM, zero markup.
📘 Documentation: https://matheusmarnt.github.io/ghostwire/ · 🇧🇷 Português · 🇪🇸 Español
Add wire:ghost to any element (or #[Ghost] to a component class, with zero view changes) and Ghostwire synthesizes a matching skeleton from the live DOM the instant a Livewire request starts — no hand-written placeholder markup, no layout shift, and it works identically whether your app runs Livewire 3.6+ or 4.x.
Features
- Zero-markup synthesis — walks the live DOM, classifies text/heading/media/control/container/panel nodes, and emits a matching Bone Tree, all in one batched read pass (no forced reflow)
- Dual Livewire bridge — Livewire 3.6+ and 4.x supported from the same package, selected by runtime feature detection (never a version-string check)
- Morph-safe — the Ghost Layer mounts outside Livewire's reconciled tree; concealment uses only
visibility/opacity/pointer-events, never a structural DOM change freezemode — dims and disables the live host in place, for layouts synthesis can't safely cover#[Ghost]attribute — class- or method-level, a 7-level precedence cascade (directive modifier → directive expression → method → class → inherited → config → package default), zero view changes required- Silence by default — sync-only and polling messages never trigger a ghost, so background updates stay invisible
- Repeat-sibling sampling, scrollable clipping, sticky/fixed geometry — real layouts (paginated tables, kanban boards, scrollable panels) synthesize correctly, not just simple cards
- Theming — shimmer/pulse/wave animations, automatic dark mode,
prefers-reduced-motionsupport, all pure CSS behind data/CSS-variable tokens - Accessibility —
aria-busy, focus preservation across the ghost window, a shared live region announcing loading/idle state; axe-core clean at the strictest level - CSP-safe — runs under a strict Content-Security-Policy (no inline scripts, no
eval); stylesheet nonce support built in php artisan ghost:inspect— see exactly which precedence level decided each component's configurationphp artisan ghost:install— publish assets and get a printed reminder of the two required layout directives in one step- Learning — remembers a component's synthesized skeleton locally (opt-in, refused in production) so a lazy-loaded component's first paint on a later visit already has a matching placeholder — export it with
php artisan ghost:exportas a static Blade@placeholder
Requirements & compatibility
| Supported | |
|---|---|
| PHP | 8.2, 8.3, 8.4 |
| Laravel | 12.x, 13.x |
| Livewire | 3.6+, 4.x |
Tier matrix (FR-81)
Bridge selection is runtime feature detection, never a version string. Everything below is verified on both lines in CI.
| Tier | Capabilities |
|---|---|
| A — identical | Directive + every modifier including .panels, except .island · #[Ghost] in full (class, method, inheritance, precedence) · synthesis · freeze · timing (delay/hold) · sync silence · theme & tokens · accessibility · panels detection and promotion · morph coexistence |
| B — degraded on 3.x | Post-paint removal (emulated via double requestAnimationFrame) · finalization (composed from multiple hooks) · cancellation (resolved as finalization) · poll detection (origin heuristic) · per-action interception (bridge-level filter — same observable behavior) |
| C — 4.x only | Island scoping (.island) · message-skip handling |
Full detail: /docs/compat.
Installation
composer require matheusmarnt/ghostwire
php artisan vendor:publish --tag=ghostwire-assets
Add both directives to your main layout, once — usually right next to
@livewireStyles/@livewireScripts:
{{-- in <head> --}}
@ghostwireStyles
{{-- right before </body> --}}
@ghostwireScripts
Then add wire:ghost to any element inside a Livewire component:
<div wire:ghost>
{{-- your existing Livewire markup, unchanged --}}
</div>
Without both the asset publish step and the two directives above, wire:ghost is dead
markup — the runtime JS that watches for it never loads. php artisan ghost:install
runs the publish step and prints this same reminder for you.
See /docs/install for the
full first-effect walkthrough, and /playground
to try synthesis on your own markup without installing anything.
Learning
Ghostwire can remember a component's synthesized skeleton locally in the browser and reuse it the next time that component is about to lazy-load — so a later visit's first lazy paint already has a matching skeleton, not a blank placeholder. Off by default, and refused server-side in production regardless of config.
The flag only enables skeleton capture and produces no visible effect by itself — pair it with #[Ghost(lazy: true)] on your component (and Livewire's #[Lazy] for the skeleton to show on initial load) to paint the learned skeleton.
GHOSTWIRE_LEARNING=true
#[Ghost(lazy: true)]
class OrdersTable extends Component
{
// ...
}
Ghostwire.exportLearned() // in the browser: downloads ghostwire-learned.json
php artisan ghost:export --component=orders-table --breakpoint=lg --from=~/Downloads/ghostwire-learned.json
Full walkthrough, storage/privacy details, and the export command's security posture: /docs/learning.
Documentation
Full docs, live playground, and gallery: https://matheusmarnt.github.io/ghostwire/
/docs/wire-ghost— directive & modifiers/docs/ghost-attribute—#[Ghost]attribute & precedence/docs/learning— local persistence, lazy skeletons,ghost:export/docs/choosing— directive vs. attribute/docs/compat— Livewire 3/4 tier matrix/docs/theming— tokens, dark mode, animation/docs/how-it-works— the synthesis algorithm/docs/security— CSP, transport, supply chain/docs/testing— Pest helpers & browser tests/docs/interop—@placeholder, Wirebones, Flux, livecharts, scoutify
Security
See SECURITY.md and /docs/security. No DOM data ever leaves the browser; learning is local-only; zero telemetry (FR-93).
Other packages by the author
License
MIT © Matheus Mariano. See LICENSE.md.
Related Packages
FilaFluxKit — Laravel 13 + Filament v5 starter kit with multi-panel structure (A...
Automatically lock Livewire properties from client-side modification unless expl...
High-performance Livewire component for searchable select inputs with relevance-...
Skeleton loading placeholder component for Laravel - supports Livewire and Vue