Refined Template Separation: Layouts and Partials in PHP
Stop repeating your HTML. Learn to build a master layout and reusable partials to keep your PHP views clean, maintainable, and DRY.

Previously in this course, we covered implementing the MVC view layer in PHP, where we learned to separate logic from presentation by passing variables into simple view files. While that works for small projects, you've likely noticed that copying your <head>, navigation, and footer into every single view file is tedious and prone to errors.
In this lesson, we are "refining" that process. We will move from basic file includes to a formal layout-and-partial architecture, ensuring that changing your navigation bar only requires editing one file rather than a dozen.
From Simple Includes to Master Layouts
A "Master Layout" acts as the shell of your application. It contains the structural HTML—the <html> tag, the <body> wrapper, and all your global assets (CSS/JS)—while providing a "slot" where your specific page content gets injected.
Think of it as a picture frame. The frame (layout) stays the same, but you can swap out the painting (the view) inside it.
The Anatomy of a Layout
Let's create a views/layout/app.php file. This file will be responsible for the boilerplate HTML:
PHP<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>My MVC App</title> <link rel="stylesheet" href="/assets/css/style.css"> </head> <body> <?php include __DIR__ . '/../partials/header.php'; ?> <main> <?php echo $content; ?> </main> <?php include __DIR__ . '/../partials/footer.php'; ?> </body> </html>
Notice the $content variable? That is where the magic happens. We will use it to inject our page-specific HTML dynamically.
Using Partials for Reusable Components

Partials are the small, reusable pieces of your UI—like your navigation, sidebar, or footer. By keeping these in a dedicated views/partials/ directory, you maintain a single source of truth for your site’s repetitive sections.
For example, your views/partials/header.php would look like this:
PHP<header> <nav> <a href="/">Home</a> <a href="/about">About</a> </nav> </header>
When you update your navigation links here, the change propagates across every page of your application instantly.
Passing Dynamic Content to the Layout
To render a full page, we need a way to wrap our specific view (e.g., home.php) inside the master layout. We do this by capturing the output buffer of our specific view and passing it into the layout variable.
In your controller, you can implement a simple rendering method:
PHPpublic function render(string $view, array $data = []) { #6A9955">// Extract variables so they are accessible in the view extract($data); #6A9955">// Start output buffering ob_start(); include __DIR__ . "/../views/{$view}.php"; $content = ob_get_clean(); #6A9955">// Include the layout, which prints the $content variable include __DIR__ . "/../views/layout/app.php"; }
Worked Example: Rendering the Home Page
If you have a home.php view file containing:
PHP<h1>Welcome to <?php echo $title; ?></h1> <p>This content is inside the layout!</p>
Your controller calls it like this:
PHP$controller->render('home', ['title' => 'My Awesome App']);
The render function captures the <h1> and <p> tags into the $content variable, then triggers the master layout to wrap them in the header, footer, and <html> tags.
Practice Exercise
- Create a
views/partials/directory in your project. - Move your existing navigation HTML into
views/partials/header.phpand your copyright info intoviews/partials/footer.php. - Create
views/layout/app.phpas shown above. - Update your controller's view-rendering logic to use the
ob_start()andob_get_clean()pattern to wrap views inside this new layout.
Common Pitfalls

- Pathing Errors: When using
include, remember that relative paths are resolved relative to the file doing the including, not the browser URL. Always use__DIR__to define absolute paths relative to the current file. - Buffer Leaks: If you forget to call
ob_get_clean(), your page might render in the wrong order or appear broken. Always ensure you close your output buffer properly. - Variable Scope: If you define a variable inside a function (like the controller), it won't be visible inside your template unless you use
extract()or pass the data explicitly.
FAQ
Q: Why use output buffering instead of just including files? A: Output buffering allows the "child" view to render first, capturing its HTML into a variable. This gives the "parent" (the layout) full control over where that content appears on the page.
Q: Can I nest layouts? A: Yes, but keep it simple. Over-engineering your template hierarchy can make debugging difficult. Start with one master layout, and use partials for everything else.
Recap

We've evolved our view layer from simple, fragmented files into a robust system:
- Master Layouts provide the skeletal structure.
- Partials handle repetitive UI components.
- Output Buffering allows us to inject dynamic content into the layout container.
Up next, we will finalize our MVC integration by connecting our finalized routes to these controllers and templates to build a fully functional application.
Work with me

Laravel REST API Development
Clean, secure, well-documented Laravel REST APIs — the backend engine for your app, mobile client, or SaaS. Built by an API specialist.

FilamentPHP Admin Panel & Dashboard Development
A powerful admin panel for your Laravel app — built with FilamentPHP so you can manage everything without touching the database.


