Creating Flexible Layouts in Laravel with Yields, Includes and Slots

David Carr

Laravel Framework

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. 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?

$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.

Read articles directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Copyright © 2006 - 2024 DC Blog - All rights reserved.