Back to Blog
Lesson 33 of the PHP: Modern PHP from the Ground Up course
PHPAugust 21, 20264 min read

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.

PHPMVCTemplatesWeb DevelopmentBackend
A close-up view of PHP code displayed on a computer screen, highlighting programming and development concepts.

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

Detailed image of assorted industrial bolts and screws for recycling or mechanics.

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:

PHP
public 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

  1. Create a views/partials/ directory in your project.
  2. Move your existing navigation HTML into views/partials/header.php and your copyright info into views/partials/footer.php.
  3. Create views/layout/app.php as shown above.
  4. Update your controller's view-rendering logic to use the ob_start() and ob_get_clean() pattern to wrap views inside this new layout.

Common Pitfalls

Close-up of a triangular warning sign indicating a slippery surface, fixed to a wooden post.

  • 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

Team members presenting a project in a modern office setting with a focus on collaboration.

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.

Similar Posts