In just a few steps, deploy your Laravel application on Fly.io, you’ll be up and running in minutes!
Livewire’s new wire:navigate
directive is brilliant. It delivers the combined forces of SEO-friendly rendering, and the smoothness of an SPA powered user experience.
Today, we’ll create an SPA powered NavBar to provide seamless page navigation across nav links, and along the way, answer a very important question: how do we highlight the active link in an SPA powered nav bar?
Let’s get to it!
3 Links and a Navbar
Let’s say, we have a Laravel Livewire app. And in it, three full page components:
/* routes/web.php */
# Bind routes to Livewire components to create Full Page goodness
Route::get('/', \App\Livewire\HomePage::class );
Route::get('/about', \App\Livewire\AboutPage::class );
Route::get('/contact', \App\Livewire\ContactPage::class );
All three of these components share the same layout file which contains a Livewire component called “nav-bar” attached on top of the page:
<!-- resources/views/app.blade.php -->
<html>
<head>...</head>
<body>
+ <livewire:nav-bar />
{{ $slot }}
</body>
</html>
Thanks to this nav-bar, users get a “global” space for navigating among the three pages:
<!-- resources\views\livewire\nav-bar.blade.php -->
<nav id="dir-nav">
<a href="/">Home</a>
<a href="about">About</a>
<a href="contact">Contact</a>
</nav
This nav-bar is well and good, but it still has room for improvement. See, moving between its links still take full-page reloads. Full page reloads disrupt the flow of user interaction— users have to wait for an entire new page to finish loading before new content can be displayed. It’s not the worst experience, but well, we can still definitely improve it.
Enter wire:navigate
Livewire v3 just recently (officially) released! With it comes the revolutionary wire:navigate
directive. It allows an “SPA” experience by loading the requested page in the background, and updating parts of the current page to reflect new content.
Simply attach the directive to any link:
<a href="..." wire:navigate>...</a>
And behold—A seamless navigation experience for our users right at the fingertips of a single directive:
—we get to decide when to do full page reloads OR background content loading.
—on top of that, Livewire renders initial component content with the page: this means each of our pages’ initial content is available for friendly SEO processing!
“Adaptable” attach on the go, SEO friendly SPA—that’s wire:navigate
—Livewire’s SPA on a silver platter 😎
Highlighting the Active Link
Alright, now we have an SPA powered experience for our nav-bar. This fact leads us to a very important question:
How do we highlight the active link in an SPA powered navbar? When a user clicks on a link and visits a page, we want to highlight that recently clicked link as “active”.
To go about this highlighting, we’ll have to make changes in our nav-bar: we highlight the <a>
tag that matches the current page’s identifier $page
:
<!-- resources/views/livewire/nav-bar.blade.php -->
<!-- Highlight a link if page's identifier matches its value -->
<a href="..." wire:navigate
@if($page=='...') class="underline" @endif
>...</a>
Now the question is, how exactly do we get the value that identifies the current $page
that’s active?
Let’s look at three ways:
1. New Page Title
One value that can identify the active link is the current page’s title. Livewire conveniently gives an easy way to set a full component’s title through its [#Title] attribute.
Simply include a [#Title]
attribute in every full-page components’ render function:
/* App\Livewire\HomePage.php */
#[Title('Home Page')]
public function render(){...}
/* App\Livewire\AboutPage.php */
#[Title('About Page')]
public function render(){...}
/* App\Livewire\ContactPage.php */
#[Title('Contact Page')]
public function render(){...}
And we’ll automatically receive a page’s title in the layout file through a $title
attribute.
We can then pass $title
to the nav-bar component, and fill in our $page
value:
<!-- resources/views/app.blade.php -->
<html>
<head><title>{{ $title ?? 'Page Title' }}</title></head>
<body>
+ <livewire:nav-bar :page="$title"/>
2. New Page URL
A second way to get the active link is through the page’s URL. From the NavBar component, get the page’s URL through url()->current()
. Then parse the route from this URL string, and assign it to $page
.
We set this assignment inside the render()
method so that the URL is refreshed every time the nav-bar is rendered, which happens every time we navigate to a new page:
/* App\Livewire\NavBar.php */
public function render(){
// $page comes from parsing the route from the url
$this->page = $this->getPage( url()->current() );
return view('livewire.nav-bar');
}
public function getPage( $url ){
// Get page from url
$url = parse_url($url);
if( isset($url['path']) )
return trim( $url['path'] ,'/' );
else
return '/';
}
3. window.location
, and Alpine
Is there a way we can update $page
and highlight our nav-link from the browser side of things? window.location.pathname
can identify the current page’s route from the frontend, we’ll use this to update the value of $page
client side.
To update $page
, we might try to use vanilla JavaScript. The official docs page advise running JavaScript inside its hook livewire:navigated
to run the scripts on page visit.
At the time of writing however, the caller of this hook is called before the new page’s url is pushed into window.history. This is why the route we receive inside a listener for livewire:navigated
gives the previous route instead of the new one:
/* This is not going to work for our use case: */
document.addEventListener('livewire:navigated',()=>{
/* This will log the old route, not the current one */
console.log('New url:',window.location.pathname);
});
Instead of relying on plain JavaScript which we need to place in the right point of code, we can instead use Livewire v3’s built-in AlpineJS to directly update the value of $page
.
See, Alpine provides us a “shortcut” syntax for doing JavaScript things, furthermore, it allows us to easily do our intended action at just the right point of time:
<!-- resources/views/livewire/nav-bar.blade.php -->
<nav
x-init="$wire.set('page',window.location.pathname)"
>
x-init is a way to hook into the initialization phase of an Alpine element( which Livewire components are! ). Since Alpine is initialized after transition to the new page, calling window.location
during x-init gives us the updated, new route!
What did we Accomplish?
Along our nav-bar journey, we learned about the use of the magically passed [#Title] attribute. We also learned that updates done in the render method are reflected even on wire:navigate
-ed pages. Finally, we learned a much quicker way to do updates client-side after “wire:navigated” by relying on Alpine’s directives and update syntax.
And here we are, six minutes long: we now have an SPA powered( SEO friendly ), “active link” highlighting, NavBar—Livewire v3, wire:navigate
style!