Livewire Navigate
Many modern web applications are built as "single page applications" (SPAs). In these applications, each page rendered by the application no longer requires a full browser page reload, avoiding the overhead of re-downloading JavaScript and CSS assets on every request.
The alternative to a single page application is a multi-page application. In these applications, every time a user clicks a link, an entirely new HTML page is requested and rendered in the browser.
While most PHP applications have traditionally been multi-page applications, Livewire offers a single page application experience via a simple attribute you can add to links in your application: wire:navigate
.
Basic usage
Let's explore an example of using wire:navigate
. Below is a typical Laravel routes file (routes/web.php
) with three Livewire components defined as routes:
use App\Livewire\Dashboard;
use App\Livewire\ShowPosts;
use App\Livewire\ShowUsers;
Route::get('/', Dashboard::class);
Route::get('/posts', ShowPosts::class);
Route::get('/users', ShowUsers::class);
By adding wire:navigate
to each link in a navigation menu on each page, Livewire will prevent the standard handling of the link click and replace it with its own, faster version:
<nav>
<a href="/" wire:navigate>Dashboard</a>
<a href="/posts" wire:navigate>Posts</a>
<a href="/users" wire:navigate>Users</a>
</nav>
Below is a breakdown of what happens when a wire:navigate
link is clicked:
- User clicks a link
- Livewire prevents the browser from visiting the new page
- Instead, Livewire requests the page in the background and shows a loading bar at the top of the page
- When the HTML for the new page has been received, Livewire replaces the current page's URL,
<title>
tag and<body>
contents with the elements from the new page
This technique results in much faster page load times — often twice as fast — and makes the application "feel" like a JavaScript powered single page application.
Redirects
When one of your Livewire components redirects users to another URL within your application, you can also instruct Livewire to use its wire:navigate
functionality to load the new page. To accomplish this, provide the navigate argument to the redirect()
method:
Now, instead of a full page request being used to redirect the user to the new URL, Livewire will replace the contents and URL of the current page with the new one.
Prefetching links
By default, Livewire includes a gentle strategy to prefetch pages before a user clicks on a link:
- A user presses down on their mouse button
- Livewire starts requesting the page
- They lift up on the mouse button to complete the click
- Livewire finishes the request and navigates to the new page
Surprisingly, the time between a user pressing down and lifting up on the mouse button is often enough time to load half or even an entire page from the server.
If you want an even more aggressive approach to prefetching, you may use the .hover modifier on a link:
<a href="/posts" wire:navigate.hover>Posts</a>
The .hover
modifier will instruct Livewire to prefetch the page after a user has hovered over the link for 60
milliseconds.
Prefetching on hover increases server usage
Because not all users will click a link they hover over, adding .hover
will request pages that may not be needed, though Livewire attempts to mitigate some of this overhead by waiting 60
milliseconds before prefetching the page.
Persisting elements across page visits
Sometimes, there are parts of a user interface that you need to persist between page loads, such as audio or video players. For example, in a podcasting application, a user may want to keep listening to an episode as they browse other pages.
You can achieve this in Livewire with the @persist
directive.
By wrapping an element with @persist
and providing it with a name, when a new page is requested using wire:navigate
, Livewire will look for an element on the new page that has a matching @persist
. Instead of replacing the element like normal, Livewire will use the existing DOM element from the previous page in the new page, preserving any state within the element.
Here is an example of an <audio>
player element being persisted across pages using @persist
:
@persist('player')
<audio src="{{ $episode->file }}" controls></audio>
@endpersist
If the above HTML appears on both pages — the current page, and the next one — the original element will be re-used on the new page. In the case of an audio player, the audio playback won't be interrupted when navigating from one page to another.
Please be aware that the persisted element must be placed outside your Livewire components. A common practice is to position the persisted element in your main layout, such as resources/views/components/layouts/app.blade.php
.
Preserving scroll position
By default, Livewire will preserve the scroll position of a page when navigating back and forth between pages. However, sometimes you may want to preserve the scroll position of an individual element you are persisting between page loads.
To do this, you must add wire:scroll to the element containing a scrollbar like so:
@persist('scrollbar')
<div class="overflow-y-scroll" wire:scroll>
<!-- ... -->
</div>
@endpersist
Using with analytics software
When navigating pages using wire:navigate
in your app, any <script>
tags in the <head>
only evaluate when the page is initially loaded.
This creates a problem for analytics software such as Fathom Analytics. These tools rely on a <script>
snippet being evaluated on every single page change, not just the first.
Tools like Google Analytics are smart enough to handle this automatically, however, when using Fathom Analytics, you must add data-spa="auto"
to your script tag to ensure each page visit is tracked properly:
<head>
<!-- ... -->
<!-- Fathom Analytics -->
@if (! config('app.debug'))
<script src="https://cdn.usefathom.com/script.js" data-site="ABCDEFG" data-spa="auto" defer></script>
@endif
</head>
Script evaluation
When navigating to a new page using wire:navigate
, it feels like the browser has changed pages; however, from the browser's perspective, you are technically still on the original page.
Because of this, styles and scripts are executed normally on the first page, but on subsequent pages, you may have to tweak the way you normally write JavaScript.