Layouts and partials
Layouts control the HTML skeleton for your pages. They live under the layouts/ directory.
Basic layout structure
The create command scaffolds layouts/page.html and layouts/post.html. A simplified layout looks like this:
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>Layouts and partials</title>
</head>
<body>
{{ include "partials/header.html" }}
<main>
{{ content }}
</main>
{{ include "partials/footer.html" }}
</body>
</html>
Key ideas:
{{title}}comes from front matter{{content}}is the rendered Markdown body{{navigation}}(when present) is generated HTML for the sidebar navigation{{latest_posts}}is injected on the home page if you use it incontent/index.md{{tags_list}},{{archives_list}}and{{posts_list}}are site-wide lists you can drop into any page or layout
Token replacement
Any key in a document's front matter is available to layouts as a token:
{{ key }}{{key}}(no spaces)
For example, given:
---
title: Landing
layout: page
cta_label: Get started
---
Welcome!
You can use {{ cta_label }} inside page.html.
Including partials
To reuse markup (headers, footers, sidebars), put HTML files under layouts/partials/ and include them with:
{{ include "partials/header.html" }}
Rules:
- Paths are resolved relative to the
layouts/directory - Nested includes are supported: partials can include other partials
- Unsafe paths using
..segments are ignored for safety
The create command seeds two partials:
layouts/partials/header.htmllayouts/partials/footer.html
Feel free to customize them.
Conditional blocks
Layouts support a simple conditional syntax:
{{#if featured}}
<div class="badge">Featured</div>
{{/if}}
You can also specify an else branch:
{{#if loggedIn}}
<a href="/account/">My account</a>
{{else}}
<a href="/login/">Log in</a>
{{/if}}
The condition key (featured, loggedIn) is looked up from the data available to the template (front matter plus injected fields like content, navigation, latest_posts).
Truthiness rules:
false,0, empty strings, empty arrays, empty structs, andnullare treated as false- Everything else is true
Layout selection
For each document, Markspresso chooses a layout as follows:
- If front matter specifies
layout, use that. - Else, if the document belongs to a collection with a
layoutconfigured, use that. - Otherwise, fall back to
build.defaultLayoutfrommarkspresso.json(typicallypage).
This means you can:
- Use
layout: pagefor regular pages - Use
layout: postfor blog posts - Define additional layouts (e.g.
docs.html) and opt into them per page or per collection.
CFML layouts
In addition to plain HTML layout files, Markspresso can render layouts written in CFML:
- For a layout name like
page, you can createlayouts/page.cfminstead of (or as well as)layouts/page.html. - When both exist, the
.cfmfile wins; it is executed by Lucee and receives helpful variables such ascontent,data(front matter + injected fields),page(file path, collection, canonical URL, etc.), andglobals/config.
Use CFML layouts when you want more dynamic behavior than simple token replacement and conditionals can provide.
Search UI and markspressoScripts
When you enable Lunr search via the search.lunr block in markspresso.json, Markspresso will:
- Generate a client-side search data file (by default
js/markspresso-search-data.js) that defineswindow.MarkspressoSearchDocs. - Copy the bundled
markspresso-search.jshelper (which loads Lunr from a CDN and builds the index) into your output directory. - Inject a
markspressoScriptstoken into the layout data, which expands to the<script>tags needed to load the search assets.
To add a search box to an HTML layout, include the expected markup and the
token. For example:
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>Layouts and partials</title>
</head>
<body>
<!-- your header / navigation here -->
<header>
<!-- other header content -->
<form class="search">
<input id="markspresso-input" type="search" placeholder="Search docs…">
</form>
</header>
<main>
{{ content }}
<div id="markspresso-search-results"></div>
</main>
</body>
</html>
The bundled markspresso-search.js looks for elements with these IDs:
markspresso-search-input– the<input>where users type their querymarkspresso-search-results– a container<div>where results will be rendered as links
You can either use those IDs directly, or adapt resources/utility/markspresso-search.js to target your own selectors.
In a CFML layout (e.g. layouts/page.cfm), the same script tags are available via the markspressoScripts variable:
<!doctype html>
<html>
<head>
<title>#encodeForHtml(data.title)#</title>
</head>
<body>
<cfoutput>
<!-- your header / navigation here -->
<form class="search">
<input id="markspresso-search-input" type="search" placeholder="Search docs…">
</form>
#content#
<div id="markspresso-search-results"></div>
#markspressoScripts#
</cfoutput>
</body>
</html>
Overriding built-in lists with CFML
Markspresso builds a few site-wide lists for you, you just have to surround them with {{}}:
{{tags_list}}{{archives_list}}{{posts_list}}
By default these are rendered as simple <ul> elements, but you can override each one with a CFML template under layouts/lists/:
layouts/lists/tags_list.cfmlayouts/lists/archives_list.cfmlayouts/lists/posts_list.cfm
If any of these .cfm files exist in your site's layouts directory, they will be used instead of the default HTML. The templates receive the corresponding data structures (tagsIndex, archivesIndex or posts), so you are free to output whatever markup you like.