In the previous article “How to use layout components in Livewire” we went through setting up an app layout component that Livewire can find. This article goes through setting up its contents for use with Livewire by adding the Livewire tags and going through a way to manage your header and page title.
The default app layout
Now that your layout component is configured to be recognised automatically by Livewire, it’s time to setup its contents to get Livewire running.
Open up your app.blade.php
file.
If you are using a front end scaffolding like Laravel Breeze or Jetstream, or upgrading the layout file in an existing app, then you will already have a layout similar to below.
If you aren’t, you can always copy it from Laravel Breeze as it is a great starting point. But you will need to modify it as it contains asset links for app.css
and app.js
, which will need to be compiled, and that is not covered in this tutorial (just remove them if you won’t be using them).
<!DOCTYPE html>
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="csrf-token" content="{{ csrf_token() }}">
<title>{{ config('app.name', 'Laravel') }}</title>
<!-- Fonts -->
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Nunito:wght@400;600;700&display=swap">
<!-- Styles -->
<link rel="stylesheet" href="{{ asset('css/app.css') }}">
<!-- Scripts -->
<script src="{{ asset('js/app.js') }}" defer></script>
</head>
<body class="font-sans antialiased">
<div class="min-h-screen bg-gray-100">
@include('layouts.navigation')
<!-- Page Heading -->
<header class="bg-white shadow">
<div class="max-w-7xl mx-auto py-6 px-4 sm:px-6 lg:px-8">
{{ $header }}
</div>
</header>
<!-- Page Content -->
<main>
{{ $slot }}
</main>
</div>
</body>
</html>
The Livewire app layout
The first thing to do when you setup Livewire in any app, is to configure Livewire’s styles and scripts tags (see Include The Assets).
To do this, we are going to use the blade component tag syntax, so it is consistent with the app layout component we have already setup.
In your app.blade.php
file, you need to add <livewire:styles />
inside the <head>
tags. I would recommended adding it after any other css stylesheets or style tags you may have, to ensure the Livewire styles have priority, as they contain some visibility and animation keyframe styles that Livewire needs to work.
Then you need add <livewire:scripts />
to the end of you body tag right before the closing </body>
tag. This ensure’s that Livewire runs after the page has finished loading.
But there is a catch. If you are installing Livewire into an existing app, you may already have some script tags or javascript assets added to the end of your body tag. If this is the case, then you need to make sure you put Livewire’s scripts first, before any other javascript libraries or script tags. Otherwise if you try to call any of Livewire’s javascript functions from any of these script tags or javascript files, then you will get errors.
NOTE: Put Livewire’s script tags at the end of the body, just before the closing
</body>
tag but before any other javascript libraries or script tags.
Your layout file should now look something like the below. I have removed most things from the <head>
tags just to make the example shorter.
<!DOCTYPE html>
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
<head>
<!-- head content removed to keep it small -->
<livewire:styles />
</head>
<body class="font-sans antialiased">
<div class="min-h-screen bg-gray-100">
@include('layouts.navigation')
<!-- Page Heading -->
<header class="bg-white shadow">
<div class="max-w-7xl mx-auto py-6 px-4 sm:px-6 lg:px-8">
{{ $header }}
</div>
</header>
<!-- Page Content -->
<main>
{{ $slot }}
</main>
</div>
<livewire:scripts />
<!-- Any other script tags or libraries here -->
</body>
</html>
With this setup Livewire will auto populate {{ $slot }}
with the contents of your Livewire component blade file.
So you should be all ready to go to start creating components.
The rest of this article assumes you know how to setup and use a Livewire component, if not please see Making Components and Rendering Components in the Livewire docs.
But what about the header?
You may have noticed that there is a header in the default Breeze layout, which outputs the contents of the {{ $header }}
variable.
But if any of you actually tried to setup a component using this layout by now, will know that we would currently be getting an error “Undefined variable: header”.
The $header
variable is populated using a named slot (see the last section of Blade Templates - Slots).
You can populate the header slot by adding the x-slot
tag with the name header
at the top of your Livewire component blade file.
<x-slot name=“header”>
<!-- Some header content here -->
</x-slot>
<div>
<!-- Livewire component content here -->
</div>
Now you may have seen in the Livewire docs “Make sure your Blade view only has ONE root element.” (See Returning Blade Views).
This is the only exception to this rule, as the blade compiler will actually remove the <x-slot>
tags and their contents, and insert them into the correct location in the layout file.
Making the header interactive
With your header slot all configured, you may get to a point where you want to add a create button inside your page heading.
That should be simple enough, inside your Livewire component blade file, populate the <x-slot>
tags for your header with a button and add wire:click
to it . Making sure your Livewire component class has the corresponding method (but that is outside the scope of this tutorial).
<x-slot name=“header”>
<button type="button" wire:click="create">Create!</button>
</x-slot>
<div>
<!-- Livewire component content here -->
</div>
Once added, refresh your page, click “Create!” and BAM! Nothing happens…
Not what you expected.
The reason is that, although the header slot contents were saved in the Livewire component blade file, when its rendered, those contents were actually outside of the area which Livewire can control. The area it currently has control of is everything inside {{ $slot }}
.
If you look back at our layout you will see {{ $header }}
is above this, so the blade compiler removes the header <x-slot>
tags and contents from our Livewire component blade file, and adds them into the layout here.
This makes it inaccessible to Livewire which means the wire:click
on the button won’t do anything.
How do we fix this?
First we change the body tags in our app.blade.php
layout file to only have a slot and Livewire scripts (and any other javascript libraries and script tags).
<!DOCTYPE html>
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
<head>
<!-- head content removed to keep it small -->
<livewire:styles />
</head>
<body class="font-sans antialiased">
{{ $slot }}
<livewire:scripts />
<!-- Any other script tags or libraries here -->
</body>
</html>
Then we can create a page layout component with everything we just took out of the body tag.
Create a file called page.blade.php
in out layouts folder.
resources/views/layouts/page.blade.php
Open it and add all the layout information we removed from the body tags (note it will also have it’s own {{ $slot }}
tag).
<div class="min-h-screen bg-gray-100">
@include('layouts.navigation')
<!-- Page Heading -->
<header class="bg-white shadow">
<div class="max-w-7xl mx-auto py-6 px-4 sm:px-6 lg:px-8">
{{ $header }}
</div>
</header>
<!-- Page Content -->
<main>
{{ $slot }}
</main>
</div>
Again to get this to work as a component, we would have to follow the same process as we used in the previous article, to create an inline blade component, this time for the page layout.
# Creates app/View/Components/PageLayout.php
php artisan make:component PageLayout --inline
Open PageLayout.php
and change the render method to point to the new page layout view.
// app/View/Components/PageLayout.php
public function render()
{
// Resolves to resources/views/layouts/page.blade.php
return view('layouts.page');
}
Then back in your Livewire component blade view, just wrap everything you had in your <x-page-layout>
tags.
<x-page-layout>
<x-slot name=“header”>
<button type="button" wire:click="create">Create!</button>
</x-slot>
<div>
<!-- Livewire component content here -->
</div>
</x-page-layout>
Now go back to your browser, refresh, and click on that “Create!” button in the header.
It should now work! Success!
The page title
The last thing which is common to want to set, in your app layout, is the page title.
The default title in the head section of the Breeze layout looks like this.
<title>{{ config('app.name', 'Laravel') }}</title>
But you may want to specify a different title for each page of your app, or have a default if there is no title specified.
So I would recommend changing it to a named slot, for title, with a fall back of your app name.
<title>{{ $title ?? config('app.name', 'Laravel') }}</title>
This will allow us to populate the title of the page by passing it through the title named slot.
There are two ways to do this with Livewire which I will list below.
But be aware, no matter which method you use, Livewire will only set the title of the page on the first load. So if you try to change it in a subsequent request, within the same component, it won't change. If you need to do that, you will need to use javascript to do this.
Livewire will only set the title of the page on the first load.
One way is to add the title named slot, which contains your desired title, to the top of our Livewire component's blade view, like we originally did for header (but outside the page layout tags). The title will only take a string so I wouldn't be trying to add any html to this.
<x-slot name="title">Our new page title</x-slot>
<x-page-layout>
<x-slot name=“header”>
<button type="button" wire:click="create">Create!</button>
</x-slot>
<div>
<!-- Livewire component content here -->
</div>
</x-page-layout>
The other way is to specify it in the render method in your component class, by passing it as a data property into the layout method (see Configuring the Layout Component).
public function render() {
return view('sample-component')
->layout('layouts.app', ['title' => 'Our new page title']);
}
The downside to this method is you need to specify the app layout for this to work.
Your app layout should now be all configured and ready to go.
If you have any questions or I have missed anything, please let me know.
Hope this helps!