Create a page layout

Use an existing site base template as your starting point. Put shared document structure in the base and page-specific markup in blocks.

{% extends "base.tpl" %}
{% block content %}
<main>
    <h1>{_ Our garden _}</h1>
    <p>{_ Find out what is growing this week. _}</p>
</main>
{% endblock %}

Check which blocks your chosen base provides before overriding one. Keep the standard head and body includes so active modules can contribute scripts, styles, metadata, and browser initialization. In a custom base, use tag#all_include with the standard include names instead of copying module-specific head fragments.

In a custom base, put {% all include "_html_head.tpl" %} inside <head>. Near the end of <body>, keep {% all include "_html_body.tpl" %}, the site's _js_include.tpl, and one final {% script %} in their established order. Wires collect browser code for that final script tag; without it a page can render correctly while its buttons do nothing.

Reuse markup with includes and categories

Extract repeated markup into a partial and pass its inputs explicitly.

{% include "_garden_card.tpl" id=id %}

Inside the partial, read the resource using m.rsc[id]. Keep the partial focused on one component so another page can reuse it without inheriting unrelated page behavior.

Use a category include when the representation depends on the resource's category:

{% catinclude "_garden_card.tpl" id %}

Provide a generic fallback and add category-specific variants when their markup differs. Use the template selection tools to check which variant wins for the resource. See tag#include, tag#catinclude, and Find the template used by a page.

For the example above, save the fallback as priv/templates/_garden_card.tpl. An event-specific version is _garden_card.event.tpl. A resource named garden_open_day can override both with _garden_card.name.garden_open_day.tpl. Test an event and an ordinary text resource: the latter should still use the fallback.

Display values safely

Resource properties read through m.rsc follow Zotonic's content handling. Query arguments and values returned by custom models do not automatically have the same guarantees.

Escape plain text from those sources:

<p>{{ q.term|escape }}</p>

Do not mark arbitrary user input as safe HTML. Decide at the model or content boundary whether a field contains text, sanitized HTML, a URL, or another type. Keep that decision consistent across templates and API responses.

When a value renders unexpectedly, inspect its type and translation behavior before applying more filters. See filter#escape, Expose data through a model, and Keep permission checks at the boundary.

Cache a template fragment

First measure the work performed by the fragment. Cache a fragment only when you know which inputs and data changes affect its output.

Check tag#cache for the supported duration, variation, and dependency arguments. A fragment containing a user's private information must not be shared between users through an incomplete cache key. Language and resource identity can also change the result.

Test a cache hit, then change the underlying content and check invalidation. During debugging, the Development page can disable template cache tags. Restore that setting before measuring normal page behavior. See Distinguish stale cache data from stale code and Choose checks for a change.

For a public resource card, a starting point is:

{% cache 60 garden_card vary=id vary=z_language if_anonymous %}
    <a href="{{ id.page_url }}">{{ id.title }}</a>
{% endcache %}

The resource ID varies the entry and is a cache dependency; language separates translations. This example caches only anonymous requests. If the fragment also reads connected resources, add the corresponding dependencies. Rename a shared cache block when its output contract changes.

Referred by

Category

Tags

Template language constructs for control flow, inclusion, inheritance, wiring, and output.

Developer guide

Create a reusable module

Create apps_user/zotonic_mod_garden with the following files. This example adds a discoverable module without starting an extra process.

Developer guide

Keep permission checks at the boundary

Use the request context when checking access to data and actions. A hidden button is not an authorization check: callers can invoke a model or endpoint…

Category

Filters

Template value transformations for text, collections, dates, numbers, URLs, and other data.

Cookbook

Add a filter or rendering component

Use a filter for a small transformation of an input value. Keep missing and unexpected values predictable, and escape output at the point where it becomes HTML.

Cookbook

Override a module template

Find the active template and its path relative to priv/templates. Create the same relative path in your site or another active module with a higher selection…

Developer guide

Create your first site

Create the garden blog, open it in your browser, and sign in to its admin.

Category

Models

Data APIs available from Erlang, templates, and MQTT through Zotonic's model interface.