Don't repeat your layout
Every page shouldn't copy-paste <html>, <head>, <nav>, <footer>. Create one base template, extend it everywhere.
Base template (layout)
Save this as a DB page named "layout":
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>{% block title %}My Site{% endblock %}</title>
<link rel="stylesheet" href="/style.css">
</head>
<body>
<nav>
<a href="/">Home</a>
<a href="/blog">Blog</a>
<a href="/about">About</a>
</nav>
<main>
{% block content %}{% endblock %}
</main>
<footer>
<p>© {{ "now"|date("Y") }} My Site</p>
</footer>
</body>
</html>
Child template (home page)
Save as DB page "home":
{% extends "layout" %}
{% block title %}Home — My Site{% endblock %}
{% block content %}
<h1>Welcome</h1>
<p>{{ introduction }}</p>
{% endblock %}
The child replaces only the blocks it defines. Everything else comes from the parent.
How references resolve
When you write {% extends "layout" %}, Twig searches for "layout" in this order:
- Database pages — looks for a page named
"layout" - Inline templates — templates defined with
app:set_template("layout", ...)
This means you can mix DB-backed pages with inline templates freely.
Including partials
Break large templates into small, reusable pieces:
{% include "header" %}
{% include "sidebar" with { menu: nav_items } %}
{% include "footer" %}
Each include gets its own scope. Pass variables explicitly with with.
Building a macro library
Macros are reusable functions inside templates. Create a page "macros":
{% macro input(name, label, type, value) %}
<div class="form-group">
<label for="{{ name }}">{{ label }}</label>
<input type="{{ type|default('text') }}"
name="{{ name }}"
id="{{ name }}"
value="{{ value|e }}">
</div>
{% endmacro %}
{% macro button(text, class) %}
<button class="btn {{ class|default('btn-primary') }}">{{ text }}</button>
{% endmacro %}
Use them in any template:
{% from "macros" import input, button %}
<form method="post">
{{ input("email", "Email address", "email", old.email) }}
{{ input("password", "Password", "password") }}
{{ button("Sign In", "btn-large") }}
</form>
Complete multi-page architecture
DB Pages:
"layout" → type=0: base HTML layout with blocks
"home" → type=0: {% extends "layout" %}, home page content
"blog_list" → type=0: {% extends "layout" %}, blog listing
"post" → type=0: {% extends "layout" %}, single post
"macros" → type=0: reusable form helpers
"header" → type=0: reusable site header partial
"blog_list" → type=29: Lua handler (fetches posts, calls ctx:render)
"blog_post" → type=29: Lua handler (fetches single post)
Even a complex site uses the same few patterns — extend, include, render.
Next: Forms & CSRF — secure forms with flash messages.