When I load a web page in a browser, what arrives is a single document. It might be long and include navigation plus footers and sidebars around a main column of text, but to the browser it is just one block of HTML.
When a site grows beyond a handful of pages, I am no longer writing one document but a whole set. Each page carries its own content, yet the structure repeats. The header and navigation stay consistent across the set. The footer and typography stay consistent, as does the layout.
If I were to copy that shared structure into every file, the site would be hard to maintain. A small change to the navigation or layout would require touching every page. Over time those copies drift, and the site becomes inconsistent. This is the practical problem templates exist to solve for me.
A template lets me write the shared structure once and reuse it. I define a common outer document with <html> and <head>, plus a shared header and navigation. The footer sits in the same frame, and I leave one or more places for page-specific content. Each article or page then supplies its own content, and the build places it into those slots.
In its simplest form, templating is just composition. A page is the result of taking some content and placing it inside a larger HTML frame. The content and the frame are separate files, but the output is a single document.
This separation matters because it lets me work on content and structure independently. I can write or revise an article without touching the site chrome. A change to the site chrome does not require me to rewrite the archive. The template system is what connects those two strands.
Once a site grows beyond a few pages, this quickly becomes essential. Without templates, I either duplicate markup everywhere or invent ad-hoc scripts to assemble pages. Templates are the conventional way to express that assembly.
This baseline matters because the rest of the series argues for a stricter, stamp-like approach to templates.
Where templates get more complicated is in how much responsibility I give them. Many systems ask templates to do more than place content into a frame. They ask templates to decide which pieces of content should appear and in what order, sometimes with conditions. That turns the template into a control layer with decisions baked in.
Whether that is a good idea depends on what I want from my publishing system. That is why I want templates to act as mechanical stamps that receive prepared content. I use the next piece to make that trade-off explicit.
In the next article I look at how that additional responsibility changes what templates are, and why it affects the clarity and predictability of the final HTML. If you want to continue, head to When Templates Start Making Decisions.
If you missed the first piece, start with Why Websites Need Templates. It lays out the baseline that this piece builds on, and it frames why templates exist in the first place.
Once I accept a template as a shared frame for many pages, it becomes tempting to push more work into it. The template already knows about layout, so it starts to feel natural to let it decide which pieces of content appear where. That is how most templating systems evolve, and the shift tends to feel gradual and understated.
At that point the template stops being a simple frame that inserts finished content into a slot. It gets access to a collection of articles and tags plus their metadata. It loops over those items and filters or sorts them, then decides whether certain blocks of HTML should appear. The template now carries structure along with selection and ordering.
At a small scale this feels convenient. If I want a list of recent posts in a sidebar, it seems reasonable to write a loop in the template that iterates over the latest five articles and add a conditional to hide a heading when there are no results. Over time these small decisions accumulate and the behaviour spreads across the layout.
Here is the template logic I am talking about:
{% if featured_posts.length > 0 %}
<h2>Featured</h2>
<ul>
{% for post in featured_posts %}
<li><a href="{{ post.url }}">{{ post.title }}</a></li>
{% endfor %}
</ul>
{% endif %}
Short posts I want to surface on the home page.
The result is that the final HTML page is no longer a simple combination of a frame and a piece of content. The output becomes a program embedded inside the template. To understand what the page will look like, I have to consider the template file. I also have to consider the data it receives and the logic that operates on that data. Reading the page becomes dependent on the build.
One consequence is that the template stops being a reliable description of the document. I can open it in an editor and see a mix of HTML and logic, yet I cannot tell which sections will appear in the output for a given build. Another consequence is that it becomes harder to reason about changes, because a small edit in the template or a change in metadata can reshape the output. This is where I start losing trust in the page before I even render it.
Many systems are built this way and people learn to work within those constraints. The template becomes a layer in the site's control plane because it now contains behaviour as well as structure. The file reads like a small program that emits a document.
At that point I find it difficult to talk about a template as a document. The distinction matters if I care about inspecting and understanding the output over time, and about preserving it as a stable artefact.
The next article describes an alternative approach: keeping selection and ordering out of templates entirely, and using them only as fixed stamps that receive already-prepared content. It is the simplest way I have found to keep layout and selection separate. Continue to Templates as Mechanical Stamps.
Before the build touches any template, it walks the filesystem and reads frontmatter to construct an index of all available articles. Queries apply to that index to produce explicit, ordered lists of article records. Each query has a name, and that name refers to a specific deterministic result set.
By the time a template enters the pipeline, the interesting work is already complete. It receives the result of a query with a list of article records that already sit in the correct order, whether the list is empty or not.
That is what lets templates stay simple. It keeps the scope small when I need to debug.
A template contains ordinary HTML and one or more <template> tags that act as render slots. Each slot names a query so the build knows what to stamp. At build time, the system takes the results of that query and stamps the corresponding content into the slot. If the query returned nothing, the system uses the fallback HTML inside the <template> tag instead. After stamping, the build removes the <template> tag itself, leaving only normal HTML behind.
Because of this, a template never needs conditionals or loops. It never needs to know how many articles exist or whether a tag is empty, and it does not decide ordering. The build resolves all of that earlier. The template defines where the stamped output should appear and what should show up when there is nothing to stamp.
This turns templating into a mechanical operation. Given the same set of article records and the same queries, the same templates will always produce the same HTML. There is no hidden state and no branching behaviour inside the template files themselves.
It also makes the templates readable in a way that is hard to achieve in more conventional systems. When I open one, I see the page structure as it will exist in the final output. I see the header and navigation, then the main content with its fallback messages for empty sections. There is no embedded logic to mentally execute. The only dynamic pieces are clearly marked as slots.
That is what I mean by calling the system boring. I want the boring sections to stay readable, not clever. The templates stay simple and ignore metadata or sorting rules, while avoiding visibility flags as they provide a fixed shape that content slots into.
Here is a concrete example that matches the home page most readers expect. It is a normal page with a header and navigation plus a list of recent posts. The only special tag is the slot where the build will stamp the query results:
Everything in that file is real HTML. If I open it in a browser, it renders as a page with an empty state because the <template> tag is inert by design. When the build runs, the system replaces that one tag with the output of the latest-posts query. Three returned articles become three summaries in that space, and the build then removes the <template> tag itself. If the query returns nothing, the fallback paragraph remains and the rest of the page stays the same.
That simplicity keeps the system predictable and inspectable, and it stays durable over time. The complexity lives in the build process and the query definitions, where I can validate it and reason about it directly. The templates remain what they look like: static HTML documents with a few clearly defined places where content will appear. If you can read the template, you can understand the page.
The aim is for a reader to open a template and feel the page is already there, with only the content missing.