Choosing directive vs. attribute
The directive decides where. The attribute decides when.
- Reach for
wire:ghostwhen you’re marking up a specific element and want the skeleton scoped to exactly that subtree — the common case for a single table, card grid, or panel. - Reach for
#[Ghost]when you want a whole component’s behavior configured without touching its view at all — ideal for bulk-migrating many existing components, or for method-level overrides tied to specific actions rather than specific markup.
Scope the skeleton to one element’s subtree, right in the view:
<div wire:ghost.delay.100ms> <h2>{{ $product->name }}</h2> <p>{{ $product->description }}</p></div>Configure the whole component from the class — no view changes:
use Ghostwire\Attributes\Ghost;
#[Ghost(mode: 'synthesize', delay: 100)]class ProductCard extends Component{ public Product $product;}They compose: put #[Ghost(mode: 'freeze')] on the class as the default, then a wire:ghost.delay.50ms directive on one particularly slow-loading element inside it to override just that host’s timing. The precedence cascade in /docs/ghost-attribute resolves the combination.
If you’re migrating 60 legacy components without touching any view — put #[Ghost] on the base class they share: under the default strategy: 'opt-in', the inherited attribute instruments every subclass, so no strategy: 'global' is needed; add directive-level overrides only where a specific element needs different timing or mode than the rest of its component.