Search by

matheusmarnt / ghostwire

matheusmarnt

Automatic runtime skeleton loaders for Livewire β€” synthesized from your live DOM, zero markup.

Package info

github.com/matheusmarnt/ghostwire

Homepage

Language:JavaScript

pkg:composer/matheusmarnt/ghostwire

Statistics

Installs: 0

Dependents: 0

Suggesters: 0

Stars: 0

Open Issues: 0


README

Ghostwire

Latest Version on Packagist Tests Total Downloads License Laravel Livewire Docs

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 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
  • freeze mode β€” 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-motion support, 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 configuration
  • 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:export as 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 except .island Β· #[Ghost] in full (class, method, inheritance, precedence) Β· synthesis Β· freeze Β· timing (delay/hold/timeout) Β· sync silence Β· theme & tokens Β· accessibility Β· 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
<div wire:ghost>
    {{-- your existing Livewire markup, unchanged --}}
</div>

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.

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/

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.