DevLearningTools

MODULE 9 · LESSON 05

Layouts (Header/Footer)

Building a consistent header/footer layout across every page with cfinclude, passing page-specific data into it, and automating the wrap with Application.cfc's onRequest.

New lessons are added one at a time as the course gets built out — a graded quiz for each lesson is still on the way.

A layout is just cfinclude and cfmodule applied to a specific, very common problem: every page on a site needs the same header and footer, but repeating those cfinclude calls on every single page is exactly the kind of duplication reusable code is supposed to avoid. This lesson covers the manual pattern first, then how Application.cfc's onRequest can automate it entirely.

Learning Objectives

After completing this lesson, you'll be able to:

  • Build a page out of a shared header, page-specific content, and a shared footer using cfinclude.
  • Pass page-specific data (like a page title) into a shared header.
  • Automate the header/footer wrap for every page using Application.cfc's onRequest.

The Basic Three-File Pattern

The simplest layout is three files: a header that opens the shared HTML structure, the page's own content, and a footer that closes it.

CFScript — header.cfm
<cfoutput>
<!DOCTYPE html>
<html>
<head><title>#pageTitle#</title></head>
<body>
</cfoutput>
CFScript — footer.cfm
</body>
</html>
Tag Syntax — a page using both
<cfset pageTitle = "Home">
<cfinclude template="/layout/header.cfm">

<p>Page content goes here.</p>

<cfinclude template="/layout/footer.cfm">
NOTE

pageTitle is set before the header include specifically because cfinclude shares the calling page's scope — the header reads a variable the page set moments earlier, with no parameter mechanism involved.

A Parameterized Alternative: cfmodule Instead

If the header needs several distinct pieces of data (a title, an active nav item, a theme) rather than one variable, cfmodule reads more clearly than pre-setting a handful of variables before a cfinclude.

Tag Syntax
<cfmodule template="/layout/header.cfm" pageTitle="Home" activeNav="home">

<p>Page content goes here.</p>

<cfinclude template="/layout/footer.cfm">
NOTE

Mixing the two is completely normal — a parameterized cfmodule header alongside a plain cfinclude footer that doesn't need any page-specific data at all.

Automating the Wrap with onRequest

Repeating a header/footer cfinclude pair on every single page still means touching every page if the pattern ever changes. Application.cfc's onRequest method can wrap every page automatically, since — once implemented — it's responsible for including the requested page itself.

CFScript — Application.cfc
<cfscript>

function onRequest(targetPage) {
    include "/layout/header.cfm";
    include arguments.targetPage;
    include "/layout/footer.cfm";
}

</cfscript>
Tag Syntax — Application.cfc
<cffunction name="onRequest" returnType="void">
    <cfargument name="targetPage" type="string" required="true">
    <cfinclude template="/layout/header.cfm">
    <cfinclude template="#arguments.targetPage#">
    <cfinclude template="/layout/footer.cfm">
</cffunction>
NOTE

Every page in the application now gets wrapped automatically — no individual page needs its own header/footer cfinclude calls at all anymore.

A Real Gotcha: onRequest Takes Over Completely

The moment onRequest is implemented at all, ColdFusion no longer runs the requested page on its own — onRequest itself becomes fully responsible for including it. Forgetting the include arguments.targetPage line doesn't wrap the page in a layout; it makes the page never run at all, since nothing ever actually includes it.

NOTE

This is the same fact the Application.cfc lesson covers about onRequestStart vs onRequest — it matters specifically here because a layout built this way is usually the first real reason a project implements onRequest in the first place.

Common Beginner Mistakes

Implementing onRequest and forgetting to include the target page

Once onRequest exists, it's the only thing responsible for running the requested page — leaving out include arguments.targetPage means the actual page never executes at all, not just that it's missing its layout.

Setting layout data (like pageTitle) after the header include instead of before

cfinclude shares scope, but only with whatever's already been set by the time it runs — a variable set after the header include is simply too late for the header to see it.

Putting a </cfoutput> or similar block-closing tag in the footer to match one opened in the header

Code blocks can't span across an include boundary, the same limitation cfinclude has generally — each file's blocks have to be self-contained.

Best Practices

  • Keep layout files (header.cfm, footer.cfm) in their own directory, referenced by an absolute, mapped path rather than a relative one.
  • Reach for onRequest once a project has more than a handful of pages — maintaining the same cfinclude pair on every page stops scaling quickly.
  • Use cfmodule instead of cfinclude for a header that needs several distinct pieces of page-specific data, rather than pre-setting several loose variables.

Interview Questions

How would you build a consistent header and footer across every page in a small application?

The manual version: cfinclude a header.cfm at the top of every page and a footer.cfm at the bottom, sharing scope so the header can read page-specific variables set beforehand.

How does onRequest change how a header/footer layout gets applied?

It automates it — onRequest can cfinclude the header, include the requested page, then cfinclude the footer, all in one place, so individual pages no longer need their own header/footer includes at all.

What happens if onRequest is implemented but never includes the target page?

The requested page never runs. Once onRequest exists, ColdFusion relies on it entirely to include the page — it doesn't fall back to running the page automatically.

Why might a header use cfmodule instead of cfinclude in a layout?

cfmodule accepts real parameters (a page title, an active nav item), which reads more clearly than setting several loose variables in the calling page's scope before a plain cfinclude.

Summary

In this lesson, you built a page out of a shared header and footer with cfinclude, passed page-specific data into a header with cfmodule as an alternative, and automated the entire wrap for every page using Application.cfc's onRequest.

What's Next?

The next lesson covers Reusable Templates more generally — applying the same reuse principles to things like list rendering and repeated UI pieces, not just a page's header and footer.