Guide chapters
Templates
Cot does not require you to use any specific templating engine. However, it provides a convenient integration with a powerful engine called Rinja. Rinja is very similar to Jinja2, which itself was inspired by Django’s template engine. It allows you to build complex templates easily while providing type safety to help catch errors at compile time.
Basic Syntax
A Rinja template is simply a text file that includes both static text and dynamic content. The dynamic content is introduced using variables, tags, and filters. Below is a simple Rinja template:
We can identify the following core syntax elements:
{% ... %}
(tags): Used to control template logic, such as loops and conditionals. In the example above,for item in items
iterates over a collection nameditems
.{{ ... }}
(variables): Used to output dynamic data into the template.|capitalize
(filters): Modify the output of variables (e.g.,capitalize
converts the first character to uppercase). You can chain multiple filters if needed.
An example of the rendered output (ignoring whitespace) might be:
First item
Second item
Third item
To make variables like items
available in the template, you need to define them in your Rust code and pass them into the template.
Example
Here is a simple demonstration of templating with Rinja in Cot:
use ;
use ;
use ;
use ;
async
Template Inheritance
A common approach when using templates is to employ template inheritance. This technique lets you define a base template for shared structure and layout, and then create child templates that only override the pieces that need to differ. Rinja supports this via two main concepts:
{% extends %}
: Tells Rinja which template the current file extends (the “parent” template). This tag must appear first in the file.{% block %}
: Defines a named section in the parent template that child templates can override. By default, the block includes whatever is in the parent, but child templates may completely replace it.
Example
base.html
:
My Site
My Site
index.html
:
Home
Welcome to my site!
This is the content of the home page.
When you render index.html
, it uses the overall structure from base.html
but replaces the title
, header
, and content
blocks with its own content.
Including Templates
Beyond inheritance, Rinja also supports including other templates. This is useful for reusing small, self-contained pieces of content across multiple pages. You can include a template with the {% include %}
tag.
Defining Variables
Any template included via {% include %}
has access to the parent template’s variables. Additionally, you can define new variables with the {% let %}
tag. Rinja’s variables behave like Rust variables: they are immutable by default, but you can shadow an existing variable with the same name.
Example
hello.html
:
Hello, !
index.html
:
Rendered output:
Hello, Alice!
Hello, Bob!
URLs
Linking to other pages in your application is a frequent requirement, and hardcoding URLs in templates can become a maintenance hassle. To address this, Cot provides the cot::reverse!()
macro. This macro generates URLs based on your route definitions, validating that you’ve passed any required parameters and that the route actually exists. If you ever change your URL structure, you’ll only need to update the route definitions.
cot::reverse!()
expects a reference to the Urls
object (which you can obtain by extracting it from the request), the route name, and any parameters needed by that route.
Example
index.html
:
Home
User 42
main.rs
:
use ;
use ;
use ;
use ;
use ;
async
async
;
Control Flow and Logic
Rinja offers several tags that let you control how the template renders and apply logic. Here are the most commonly used ones:
If
Use the {% if %}
tag to conditionally render parts of the template based on a certain condition. For more complex scenarios, you can include {% elif %}
or an {% else %}
section.
Example
Welcome, admin!
Welcome, user!
Please log in to continue.
Match
The {% match %}
tag matches a value against a set of Rust patterns. Use {% when %}
to specify each pattern and its corresponding content.
Example
Welcome, admin!
Welcome, user!
For
The {% for %}
tag allows you to iterate over a sequence of items. Inside the loop, Rinja provides helpful variables such as:
loop.index
: Current iteration (1-indexed).loop.index0
: Current iteration (0-indexed).loop.first
:true
on the first iteration.loop.last
:true
on the last iteration.
Example
.
Whitespace Control
By default, Rinja preserves all whitespace, which can sometimes cause unwanted gaps in your output when using loops or conditionals. To manage this, you can use the -
modifier before or after a tag to trim surrounding whitespace.
Example
This usage of -
ensures that no extra whitespace or blank lines appear around the <li>
tags.
Comments
You can include comments in your Rinja templates using {# ... #}
. These comments are ignored in the rendered output and can be used to document logic or temporarily disable parts of your template. They also support whitespace control via -
.
Example
{# This is a comment #}
This will be rendered.
{#-
This is a multi-line comment
which won’t appear in the output.
-#}
Custom Renderable Types
To display custom types in Rinja templates, the type must implement Display
. This makes the type’s to_string()
output available in the template.
Example
main.rs
:
use ;
use ;
index.html
:
When rendered, it will display the title
from the Item
struct.
HTML Escaping
By default, Rinja escapes all output to protect against XSS attacks. Special characters are replaced with their HTML entities. If you’re certain your data is safe and want to bypass escaping, you can implement the HtmlSafe
marker trait.
use ;
Be very cautious when marking output as safe; you are responsible for ensuring that the content doesn’t introduce security risks.
To simplify generating safe HTML in Rust, Cot provides the HtmlTag
type. It automatically applies escaping where necessary.
Read More
This chapter only covers the basics of Rinja. For more detailed information, advanced usage, and additional examples, check out the Rinja documentation.