spatie/commonmark-wire-navigate
CommonMark extension that adds a wire:navigate attribute to links rendered from Markdown, enabling Livewire SPA navigation. Install via Composer and register WireNavigateExtension in your CommonMark environment or spatie/laravel-markdown config.
composer require spatie/commonmark-wire-navigate
CommonMark instance (typically in a service provider or AppServiceProvider):
use Spatie\CommonMarkWireNavigate\CommonMarkWireNavigateExtension;
use League\CommonMark\CommonMarkConverter;
$converter = new CommonMarkConverter();
$converter->getEnvironment()->addExtension(new CommonMarkWireNavigateExtension());
$markdown = '# [Click Me](#)';
echo $converter->convert($markdown);
Output:
<h1><a href="#" wire:navigate>Click Me</a></h1>
CommonMarkWireNavigateExtension – Understand how it modifies the InlineParser for links.tests – Real-world examples of usage and edge cases.wire:navigate.// In a service provider or helper
public function markdownConverter()
{
$converter = new CommonMarkConverter();
$converter->getEnvironment()->addExtension(new CommonMarkWireNavigateExtension());
return $converter;
}
{!! $this->markdownConverter()->convert($markdownContent) !!}
$extension = new CommonMarkWireNavigateExtension();
$extension->shouldAddWireNavigateToLink = function ($link) {
return !str_contains($link->getUrl(), ['external.com', '#']);
};
$converter->getEnvironment()->addExtension($extension);
wire:navigate for SPA compatibility.wire:navigate only works in SPA mode).wire:navigate only in SPA contexts:
$extension = new CommonMarkWireNavigateExtension();
$extension->shouldAddWireNavigate = fn () => app()->environment('spa');
$converter = $this->app->make(CommonMarkConverter::class);
$html = $converter->convert('# [Test](#)');
$this->assertStringContainsString('wire:navigate', $html);
SPA Mode Requirement:
wire:navigate only works in Livewire SPA mode. Forgetting to enable SPA mode (<livewire:component wire:navigate />) will make links behave like traditional anchor tags.wire:navigate attributes. If missing, verify SPA mode is active.Link URL Validation:
wire:navigate to all links by default, including:
#section).mailto:, tel:).shouldAddWireNavigateToLink to filter links (e.g., exclude external URLs):
$extension->shouldAddWireNavigateToLink = function ($link) {
return !str_starts_with($link->getUrl(), ['http://', 'https://', 'mailto:', 'tel:']);
};
Nested Markdown Parsers:
spatie/laravel-markdown), ensure this extension is added last to avoid conflicts:
$converter->getEnvironment()->addExtension(new CommonMarkWireNavigateExtension()); // Last!
Performance:
Missing Attributes:
$converter->getEnvironment()->hasExtension(CommonMarkWireNavigateExtension::class);
Unexpected Links:
dd($link->getUrl()) in a custom shouldAddWireNavigateToLink callback to inspect problematic links.Customize Link Filtering:
$extension->shouldAddWireNavigateToLink = function ($link) {
return $link->getUrl() === '#' || str_contains($link->getUrl(), '/admin/');
};
Add Additional Attributes:
Extend the extension to include other Livewire attributes (e.g., wire:click):
use League\CommonMark\Extension\CommonMark\Node\Inline\Link;
use League\CommonMark\Renderer\NodeRendererInterface;
class CustomWireNavigateExtension extends CommonMarkWireNavigateExtension
{
public function register(NodeRendererInterface $renderer): void
{
$renderer->addRenderer(
Link::class,
fn (Link $link, RenderContext $context) => '<a href="' . $link->getUrl() . '" wire:navigate wire:click="customAction">...</a>'
);
}
}
Conditional Activation:
$extension = new CommonMarkWireNavigateExtension();
$extension->shouldAddWireNavigate = fn () => auth()->check() && auth()->user()->is_admin;
$extension->shouldAddWireNavigate = fn () => request()->hasHeader('x-spa-mode');
$extension->shouldReplaceExistingAttributes = true;
How can I help you explore Laravel packages today?