Template Inheritance with @extends

+15 Mana ✨

Introduction

Blade's template inheritance system lets a child view slot its content into a master layout via @extends and @section, keeping the HTML boilerplate (doctype, head, nav, footer) in one place.

Key Concepts

  • Master layout: A Blade view containing shared structure (doctype, head, nav, footer) and @yield placeholders for child content.
  • @extends / @section: The child-view side of inheritance — declares which layout to extend and fills named placeholders.
  • @include: Embeds a partial view at the point of inclusion, optionally passing data.
  • @stack / @push: A layout defines a named stack; child views @push content into it (commonly used for page-specific CSS/JS).

Real World Context

Without template inheritance, every page duplicates the <html>, <head>, navigation, and footer markup — and every design tweak touches dozens of files. Layouts eliminate that duplication and make global changes a one-file edit.

Deep Dive

Blade's template inheritance lets you define a master layout that child views can extend. This eliminates duplication and ensures consistency.

Creating a Layout

blade
<!-- resources/views/layouts/app.blade.php -->
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>@yield('title', 'My App')</title>

    @vite(['resources/css/app.css', 'resources/js/app.js'])

    @stack('styles')
</head>
<body>
    <nav class="navbar">
        @include('partials.navigation')
    </nav>

    <main class="container">
        @yield('content')
    </main>

    <footer>
        @include('partials.footer')
    </footer>

    @stack('scripts')
</body>
</html>

Extending a Layout

blade
<!-- resources/views/posts/index.blade.php -->
@extends('layouts.app')

@section('title', 'All Posts')

@section('content')
    <h1>All Posts</h1>

    @foreach ($posts as $post)
        <article>
            <h2>{{ $post->title }}</h2>
            <p>{{ $post->excerpt }}</p>
        </article>
    @endforeach
@endsection

@yield and @section

@yield

Define a placeholder in the layout:

blade
{{-- Simple yield --}}
@yield('content')

{{-- With default value --}}
@yield('title', 'Default Title')

{{-- With default content block --}}
@yield('sidebar', View::make('partials.default-sidebar'))

@section

Fill the placeholder in child views:

blade
{{-- Single line --}}
@section('title', 'Page Title')

{{-- Multi-line content --}}
@section('content')
    <h1>Welcome</h1>
    <p>Content here...</p>
@endsection

@section with @show vs @endsection

blade
{{-- In layout: Define section with default content --}}
@section('sidebar')
    <p>This is the default sidebar.</p>
@show  {{-- Use @show to display immediately --}}

{{-- In child: Can override OR extend --}}
@section('sidebar')
    <p>Custom sidebar content</p>
@endsection

Extending Parent Sections with @parent

blade
{{-- Layout --}}
@section('sidebar')
    <div class="sidebar-header">Menu</div>
@show

{{-- Child view --}}
@section('sidebar')
    @parent  {{-- Include parent's content --}}
    <nav>Custom navigation</nav>
@endsection

{{-- Result: --}}
<div class="sidebar-header">Menu</div>
<nav>Custom navigation</nav>

@include for Partials

Include reusable view fragments:

blade
{{-- Basic include --}}
@include('partials.navigation')

{{-- Pass data to the partial --}}
@include('partials.user-card', ['user' => $user])

{{-- Include if exists --}}
@includeIf('partials.optional')

{{-- Include when condition is true --}}
@includeWhen($user->isAdmin, 'partials.admin-menu')

{{-- Include unless condition is true --}}
@includeUnless($user->isGuest, 'partials.user-sidebar')

{{-- Include first existing view --}}
@includeFirst(['custom.header', 'partials.header'])

@each for Collections

Render a view for each item in a collection:

blade
{{-- Basic each --}}
@each('partials.post-card', $posts, 'post')

{{-- With empty state --}}
@each('partials.post-card', $posts, 'post', 'partials.no-posts')

Equivalent to:

blade
@forelse ($posts as $post)
    @include('partials.post-card', ['post' => $post])
@empty
    @include('partials.no-posts')
@endforelse

Stacks for Scripts and Styles

Push content to named stacks:

blade
{{-- Layout --}}
<head>
    <link href="/css/app.css" rel="stylesheet">
    @stack('styles')
</head>
<body>
    @yield('content')

    <script src="/js/app.js"></script>
    @stack('scripts')
</body>

{{-- Child view --}}
@push('styles')
    <link href="/css/posts.css" rel="stylesheet">
@endpush

@section('content')
    <h1>Posts</h1>
@endsection

@push('scripts')
    <script src="/js/posts.js"></script>
@endpush

{{-- Prepend to stack --}}
@prepend('scripts')
    <script>var postId = {{ $post->id }};</script>
@endprepend

Complete Layout Example

blade
<!-- resources/views/layouts/app.blade.php -->
<!DOCTYPE html>
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta name="csrf-token" content="{{ csrf_token() }}">

    <title>@yield('title') - {{ config('app.name') }}</title>

    @vite(['resources/css/app.css', 'resources/js/app.js'])
    @stack('styles')
</head>
<body class="@yield('body-class')">
    <header>
        @include('partials.navigation')
    </header>

    @hasSection('hero')
        <section class="hero">
            @yield('hero')
        </section>
    @endif

    <main class="container mx-auto py-8">
        @if (session('success'))
            <div class="alert alert-success">
                {{ session('success') }}
            </div>
        @endif

        @yield('content')
    </main>

    <footer>
        @include('partials.footer')
    </footer>

    @stack('scripts')
</body>
</html>

Common Pitfalls

  1. Mixing @extends with Blade components in the same file — They're two different composition models. Pick one per view.
  2. Calling @show instead of @endsection — @show both ends the section and immediately yields its content, which is only what you want in the layout itself (for default section content).
  3. Using @parent without understanding it — @parent is only meaningful when a child view wants to extend, not replace, a section with default content.

Best Practices

  1. One base layout per major UI context — E.g., layouts/app.blade.php for the authenticated app, layouts/guest.blade.php for the marketing site.
  2. Use @stack for page-specific CSS/JS — Keeps the base layout clean while letting individual pages inject what they need.
  3. Prefer @include for truly-shared partials — A single navigation partial beats pasting nav markup into every layout.

Summary

  • Layouts define the shared structure with @yield('section') placeholders.
  • Child views call @extends('layouts.app') and fill placeholders with @section('section') … @endsection.
  • @include, @includeIf, @includeWhen, and @each embed partials for repeated UI.
  • @stack + @push let child pages inject page-specific CSS/JS into the layout.
  • @parent extends rather than replaces a parent section's default content.
✓ Completed