Creating Flexible Layouts in Laravel with Yields, Includes, and Slots
Yields
In Laravel, the @yield
directive is used in blade templates to define a section that can have code injected or "yielded" by child views.
Here's a basic explanation of how @yield
works:
Defining a section with @yield('name')
In a blade file typically used for layouts (can be any *.blade.php file), you can use the @yield
directive to define a section where content can be injected. For example:
layouts/app.blade.php
<html>
<head>
<title>@yield('title') - {{ conifig('app.name') }}</title>
</head>
<body>
<div class="container">
@yield('content')
</div>
</body>
</html>
In this example, there are two @yield
directives: one for the 'title' section and another for the 'content' section.
In a child view that extends the parent view, you can use the @section
directive to fill the sections defined in the parent view.
For example:
@extends('layouts.app')
@section('title', 'Page Title')
@section('content')
<p>This is the content of the page.</p>
@endsection
In this example, the view extends from layouts/app.blade.php
the blade.php
is left off the path as Laravel will know the file by its name only.
Then fill the 'title' and 'content' sections using a @section
directive. This directive can self-close like in the title example or enclose multiple lines like in the ‘content’ example.
When you render the child view, Laravel will combine the content of the child view with the layout defined in the parent view. The @yield
directives in the parent view will be replaced with the content provided in the child view.
php
The resulting HTML will be:
<html>
<head>
<title>Page Title</title>
</head>
<body>
<div class="container">
<p>This is the content of the page.</p>
</div>
</body>
</html>
Includes
For more flexibility, you can break your views into smaller chunks using multiple views, for example:
@include('layouts.partials.header')
<div class="wrapper">
@yield('content')
</div>
@include('layouts.partials.footer')
This layout view uses both @include
and @yield
directives. An include is used to bring in other files into the current file where the @include
is placed.
@include
takes 2 arguments. A file path and optionally an array. By default, anything defined in the parent page is available to the included file but it’s better to be explicit by passing in an array.
For example:
@include('pages', ['items' => $arrayOfItems])
The above example would include a pages.blade.php
file and pass in an array called items. Inside the view $pages
could then be used.
@include('layouts.partials.header')
would include a file called header.blade.php
located in layouts/partials
Typically I define multiple yields
in a header like this:
<html>
<head>
@yield('meta')
<title>@yield('title') - {{ config('app.name') }}</title>
@yield('css')
</head>
<body>
</body>
@yield('js')
</html>
This allows me to inject code into various parts of my layout.
For example in a blog I want to use meta tags for that post only and no other pages. This means I cannot hardcode the meta tags and the contents should change from post to post.
Typically I would inject meta tags in a post view like this:
@section('meta')
<meta itemprop="name" content="{{ $post->title }}">
<meta itemprop="description" content="{!! strip_tags(Str::limit($post->description, 100)) !!}">
@if (!empty($post->image))
<meta itemprop="image" content="{{ url($post->image) }}">
@endif
<meta name='description' content='{!! strip_tags(Str::limit($post->description, 100)) !!}'>
<meta property="article:published_time" content="{{ $post->created_at }}" />
<meta property="article:modified_time" content="{{ $post->updated_at }}" />
<!-- Open Graph / Facebook -->
<meta property="og:type" content="website">
<meta property="og:url" content="{{ url($post->slug) }}">
<meta property="og:title" content="{{ $post->title }}">
<meta property="og:description" content="{!! strip_tags(Str::limit($post->description, 100)) !!}">
@if (!empty($post->image))
<meta property="og:image" content="{{ url(trim($post->image)) }}">
@endif
<!-- Twitter -->
<meta property="twitter:card" content="summary_large_image">
<meta name="twitter:site" content="@dcblogdev">
<meta name="twitter:creator" content="@dcblogdev">
<meta property="twitter:url" content="{{ url($post->slug) }}">
<meta property="twitter:title" content="{{ $post->title }}">
<meta property="twitter:description" content="{!! strip_tags(Str::limit($post->description, 100)) !!}">
@if (!empty($post->image))
<meta property="twitter:image" content="{{ url(trim($post->image)) }}">
@endif
<link rel="canonical" href='{{ url($post->slug) }}'>
<link rel="webmention" href='https://webmention.io/dcblog.dev/webmention'>
<link rel="pingback" href="https://webmention.io/dcblog.dev/xmlrpc" />
<link rel="pingback" href="https://webmention.io/webmention?forward={{ url($post->slug) }}" />
@endSection
I may also inject CSS or Javascript from a single view, this is done in the same way:
@section('js')
<script>
...
</script>
@endSection
Slots
When working with components or Livewire you will come across the concept of slots.
What is a slot?
A $slot
variable is a special variable used to reference the content that is passed into a component. Components are a way to create reusable and encapsulated pieces of view logic.
Here's a brief explanation of how $slot
works within Laravel components:
Components
When you create a Blade component, you can define a slot within it using the {{ $slot }}
syntax.
For example:
<!-- resources/views/components/alert.blade.php -->
<div class="alert">
{{ $slot }}
</div>
In this example, the alert component has a slot where content can be injected.
When you use the component in another view, you can pass content into the slot using the component tag.
For example:
<!-- resources/views/welcome.blade.php -->
<x-alert>
This is the content for the alert.
</x-alert>
In this example, the content "This is the content for the alert." is passed into the $slot
of the alert component.
When the Blade view is rendered, Laravel will replace the {{ $slot }}
in the component with the content provided when using the component. The resulting HTML will look like this:
<div class="alert">
This is the content for the alert.
</div>
The $slot
variable essentially acts as a placeholder for the content passed to the component.
Using $slot
allows you to create flexible and reusable components that can accept different content each time they are used. It provides a convenient way to structure and organize your Blade templates while maintaining the reusability of components.
Using Component Layouts
Components can also be layout files, for example making a new component called AppLayout
using Artisan:
php artisan make:component AppLayout
Will create 2 files:
app/View/Components/AppLayout.php
resources/views/components/app-layout.blade.php
Replace the contents of AppLayout.php
with:
<?php
namespace App\View\Components;
use Illuminate\View\Component;
use Illuminate\View\View;
class AppLayout extends Component
{
/**
* Get the view / contents that represents the component.
*/
public function render(): View
{
return view('layouts.app');
}
}
This will load a layouts/app.blade.php
when rendered.
To have a view use this layout inside a view use the <x-
directive followed by the component name in the kebab case. So AppLayout
becomes app-layout
<x-app-layout>
The content goes here.
</x-app-layout>
Now inside app.blade.php
since this used the AppLayout
component we don’t use @yield
to have placeholders instead, we use {{ $slot }}
or named slots
For example:
<!DOCTYPE html>
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
<head>
<title>{{ $title ?? null }} - {{ config('app.name', 'Laravel') }}</title>
</head>
<body>
{{ $slot }}
</body>
</html>
What are named slots?
In components, named slots provide a way to define and pass content into specific sections of a component. Unlike the default slot, which is referenced using, named slots allow you to define multiple distinct sections for injecting content. This can be particularly useful when you need to organize and structure different parts of your component.
Here's a basic explanation of how named slots work.
When creating a blade component, you can define named slots using the @slot
directive. For example:
<!-- resources/views/components/alert.blade.php -->
<div class="alert">
<div class="header">
@slot('header')
{{ $header ?? '' }}
@endslot
</div>
<div class="content">
{{ $slot }}
</div>
<div class="footer">
@slot('footer')
{{ $footer ?? '' }}
@endslot
</div>
</div>
In this example, the alert component has three named slots: 'header', 'footer', and the default slot, which is simply referred to as $slot
.
When using the component in another blade view, you can pass content into the named slots using the component tag. For example:
<!-- resources/views/welcome.blade.php -->
<x-alert>
<x-slot name="header">Alert Header</x-slot>
This is the content for the alert.
<x-slot name="footer">Alert Footer</x-slot>
</x-alert>
Here, content is provided for the 'header' and 'footer' named slots, in addition to the default slot.
When the blade view is rendered, Laravel will replace the content of the named slots in the component with the content provided when using the component. The resulting HTML will look like this:
<div class="alert">
<div class="header">Alert Header</div>
<div class="content">This is the content for the alert.</div>
<div class="footer">Alert Footer</div>
</div>
Named slots provide a way to structure and organize the content of your components more explicitly. They make it easier to manage complex components with multiple distinct sections by giving each section a meaningful name.
Subscribe to my newsletter
Read articles from David Carr directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by
David Carr
David Carr
Blogger at http://dcblog.dev.