wire:navigate

The wire:navigate directive provides SPA-like page navigation by intercepting link clicks and loading pages in the background. Instead of full page refreshes, Livewire fetches new content and swaps it seamlessly, resulting in faster navigation and a smoother user experience.

Basic Usage

This navigation example demonstrates wire:navigate with hover prefetching and redirect functionality:

// wires/BlogPost.bx
class extends="cbwire.models.Component" {
    data = {
        "title": "",
        "content": ""
    };

    function save() {
        // Save blog post
        redirect("/blog", true); // Navigate using wire:navigate
    }

    function cancel() {
        redirect("/blog", true);
    }
}
<!-- layouts/Main.bxm -->
<bx:output>
<nav>
    <a href="/" wire:navigate>Dashboard</a>
    <a href="/blog" wire:navigate.hover>Blog</a>
    <a href="/users" wire:navigate>Users</a>
</nav>

<!-- Persisted audio player -->
<div x-persist="player">
    <audio src="#episode.file#" controls></audio>
</div>

<main>
    #wire("BlogPost")#
</main>
</bx:output>

What wire:navigate Does

When you add wire:navigate to a link, CBWIRE automatically:

  • Intercepts Clicks: Prevents browser from performing full page visits

  • Background Fetching: Requests new page content via AJAX with loading indicator

  • Seamless Swapping: Replaces URL, <title>, and <body> content without refresh

  • Performance Boost: Often achieves 2x faster page loads than traditional navigation

Available Modifiers

The wire:navigate directive supports one modifier:

  • Hover: .hover - Prefetches page content when user hovers over link for 60ms

Example: wire:navigate.hover improves perceived performance but increases server usage.

Here's what happens during navigation:

  1. User clicks a wire:navigate link

  2. Livewire prevents default browser navigation

  3. Loading bar appears at top of page

  4. Page content is fetched in background

  5. New content replaces current page seamlessly

Redirecting with Navigation

Use CBWIRE's redirect() method with navigation enabled:

function submit() {
    redirect("/thank-you", true); // Second parameter enables wire:navigate
}

Persisting Elements Across Pages

Use Alpine's x-persist to maintain elements like audio/video players:

<!-- layouts/Main.bxm -->
<bx:output>
<div x-persist="player">
    <audio src="#episode.file#" controls></audio>
</div>

<div x-persist="chat">
    <div class="chat-widget">Live chat...</div>
</div>
</bx:output>

Preserving Scroll Position

Livewire preserves page scroll by default. For specific elements, use wire:scroll:

<div x-persist="scrollbar">
    <div class="overflow-y-scroll" wire:scroll>
        <!-- Long scrollable content -->
    </div>
</div>

JavaScript Hooks

Navigation triggers three lifecycle events:

document.addEventListener('livewire:navigate', (event) => {
    // Triggered when navigation starts
    // Can prevent navigation: event.preventDefault()
    
    let context = event.detail;
    context.url;     // URL object of destination
    context.history; // True if back/forward navigation
    context.cached;  // True if cached version exists
});

document.addEventListener('livewire:navigating', () => {
    // Triggered before HTML swap
    // Good place to mutate HTML before navigation
});

document.addEventListener('livewire:navigated', () => {
    // Triggered after navigation completes
    // Use instead of DOMContentLoaded
});

Manual Navigation

Trigger navigation programmatically:

Livewire.navigate('/new/url');

Event Listeners

Handle persistent event listeners properly:

// Run once per navigation
document.addEventListener('livewire:navigated', () => {
    // Page-specific code
}, { once: true });

// Persistent across navigations
document.addEventListener('livewire:navigated', () => {
    // Global code
});

Analytics Integration

Ensure analytics track navigation properly:

<script src="https://cdn.usefathom.com/script.js" 
        data-site="ABCDEFG" 
        data-spa="auto" 
        defer></script>

Script Handling

Control script execution across navigations:

<head>
    <!-- Cache-busted scripts with tracking -->
    <script src="/app.js?id=123" data-navigate-track></script>
</head>

<body>
    <!-- Run only once -->
    <script data-navigate-once>
        console.log('Runs only on first evaluation');
    </script>
</body>

Progress Bar Customization

Configure the loading indicator:

// config/ColdBox.bx
moduleSettings = {
    "cbwire": {
        "showProgressBar": true,
        "progressBarColor": "##2299dd"
    }
};

Custom Loader Example

Create a custom progress indicator:

<script>
document.addEventListener('livewire:navigate', (event) => {
    if (!window.navigating) {
        window.navigating = true;
        event.preventDefault();
        
        let target = event.detail;
        
        // Show custom loader
        let loader = document.createElement('div');
        loader.className = 'spinner-border text-primary';
        loader.setAttribute('role', 'status');
        loader.innerHTML = '<span class="visually-hidden">Loading...</span>';
        document.getElementById('content').appendChild(loader);
        
        Livewire.navigate(target.url.pathname);
    }
}, { once: true });

document.addEventListener('livewire:navigated', () => {
    window.navigating = false;
});
</script>

Persisted elements must be placed outside CBWIRE components, typically in your main layout. Use livewire:navigated instead of DOMContentLoaded for page-specific code.

Last updated

Was this helpful?