Stop Click Hijacking: Mastering wire:navigate with Alpine Dialogs
You build a card-style CTA. It’s clickable. It should navigate when tapped.
Inside that card, you also add a tiny button to open a dialog.
Here’s the thing: because the whole card is wrapped in a navigation click, that little button doesn’t stand a chance. Users try to open the dialog… and boom. they’re already on the next page.
I didn’t go hunting strangers or asking every developer forum to bless me. I opened the docs and figured it out.
Livewire Navigation Docs
Before: The Unwanted Navigation
Typical structure:
1<a 2 wire:navigate 3 href="/somewhere" 4 x-data="{ dialogOpen: false }" 5> 6 <button x-on:click="dialogOpen = true"> 7 <x-lucide-info /> 8 </button> 9 10 <div11 x-show="dialogOpen"12 class="dialog-panel"13 >14 <p>Content...</p>15 </div>16</a>
Everything looks correct… but a button inside a clickable navigation wrapper = accidental page changes.
You click the info button ➜ navigation fires anyway
because the click event bubbles up to <a wire:navigate>.
After: Full Control Navigation Only When You Decide
Instead of letting wire:navigate automatically trigger,
we call it manually using Livewire.navigate().
1+ @script 2+ <script> 3+ window.redirectToTarget = () => { 4+ Livewire.navigate(@js(route('target.route', ['param' => $param->id]))) 5+ } 6+ </script> 7+ @endscript 8 9- <div10- x-data="{ dialogOpen: false }"11+ x-on:click="redirectToTarget"12- class="clickable-card"13- >14- <h3>15- Action Title16- <button17- type="button"18- x-on:click.stop.prevent="dialogOpen = true"19- >20- <x-lucide-info />21- </button>22- </h3>23-24- <p>Short description text</p>25-26- <div x-show="dialogOpen" class="dialog-panel">27- <h2>Dialog Title</h2>28- <p>More info goes here.</p>29- <button type="button" x-on:click="dialogOpen = false">Close</button>30- </div>31- </div>
Why This Works
- The card itself controls navigation using
redirectToTarget() - The dialog button stops the click from bubbling up (
.stop.prevent) - You decide when navigation should fire not the browser
No hacks. No overlays. Just letting Alpine handle UI, and Livewire handle navigation independently.
Key Lesson
When combining Alpine interactions inside click-based navigation:
- Stop clicks meant for UI elements
- Trigger navigation programmatically
- Keep control where it belongs
Sometimes the cleanest solution starts with simply reading the docs.