So without further ado, let's see what a typical template looks like:

[% PROCESS header title="Some Interesting Links" %]

Here are some interesting links:
[% FOREACH link = weblinks %]
   <li><a href="[% link.url %]">[% link.title %]</a></li>
[% END %]

[% PROCESS footer %]

The first thing to note is that template directives are embedded within [% and %]. You can change these values, along with several dozen other configuration options, but we'll stick with the defaults for now. The directives within those tags are instructions to the template processor. They can contain references to variables (e.g., [% link.url %]) or language constructs that typically begin with an uppercase word and may have additional arguments (e.g., [% PROCESS footer %]). Anything else outside the tags is plain text and is passed through unaltered.

The example shows the PROCESS directive being used to pull in a header template at the top of the page and a footer template at the bottom. The header and footer templates can have their own directives embedded within them and will be processed accordingly. You can pass arguments when calling PROCESS, just as you might when calling a subroutine in Perl. This is shown in the first line, where we set a value for the title variable.

By default, variables are global, and if you change title in one template, the new value will apply in any other templates that reference it. The INCLUDE directive goes a little further to make arguments more local, giving you better protection from accidentally changing a variable with global consequences. Separate variable namespaces can also be used to avoid collisions between variables of the same name (e.g., page.title versus book.title).

In the middle of the example, we see the FOREACH directive. This defines the start of a repeated block that continues until the END directive two lines below. Loops, conditionals, and other blocks can be combined in any way and nested indefinitely. In this case, we're setting the link variable to alias each item in the list referenced by the weblinks variable. We print the url and title for each item, with some appropriate HTML markup to display them formatted as an HTML bullet list.

The dot (.) operator is used to access data items within data items, and it tries to do the right thing according to the data type. For example, each item in the list could be a reference to a hash array, in which case link.url would be equivalent to the Perl code $link->{url}, or it could be an object against which methods can be called, such as $link->url( ). The dotted notation hides the specifics of your backend code so that you don't have to know or care about the specifics of the implementation. Thus, you can change your data from hash arrays to objects at some later date and slot them straight in without making any changes to the templates.

Let's now go back to our earlier example and see if we can make sense of it:

<h3>[% users.size %] users currently logged in:</h3>
[% FOREACH user = users %]
   [%# 'loop' is a reference to the FOREACH iterator -%]
   <li>[% loop.count %]/[% loop.size %]: 
       <a href="[% user.home %]">[% %]</a>
   [% IF user.about %]
       <p>[% user.about %]</p>
   [% END %]
   [% INCLUDE userinfo %]
[% END %]

Anything outside a [% ... %] directive—in this case, various HTML fragments that are building a list of users currently logged in to our fictional system—is passed through intact.

The various constructs that we meet inside the directives are:

We're assuming here that the users variable contains a reference to a list of users. In fact, it might also be a reference to a subroutine that generates a list of users on demand, but that's a backend implementation detail we're quite rightly not concerned with here. The Template Toolkit does the right thing to access a list or call a subroutine to return a list, so we don't have to worry about such things.

The users themselves (i.e., the items in the users list) can be references to hash arrays, or maybe references to objects. Again, the Template Toolkit hides the implementation details and does the right thing when the time comes.

There are a number of "virtual methods" you can call on basic Perl data types. Here, the .size virtual method returns the number of items in the users list.

FOREACH user = users
The FOREACH directive defines a block of template code up to the corresponding END directive and processes it repeatedly for each item in the users list. For each iteration, the user variable is set to reference the current item in the list.

The loop variable is set automatically within a FOREACH block to reference a special object (an iterator) that controls the loop. You can call various methods in this object, such as loop.count to return the current iteration (from 1 to n) and loop.size to return the size of the list (in this case, the same as users.size).

The user variable references each item in the users list in turn. This can be a reference to a hash array or an object, but we don't care which. Again, these details are sensibly hidden from view. We just want the home part of user, and we're not too worried about where it comes from or what has to be done to fetch it.

IF user.about
The IF directive defines a block that gets processed if the condition evaluates to some true value. Here we're simply testing to see if user.about is defined. As you might expect, you can combine IF with ELSIF and ELSE and also use UNLESS.

INCLUDE userinfo
The INCLUDE directive is used here to process and include the output of an external template called userinfo. The INCLUDE_PATH configuration option can be used to specify where external templates can be found, so you can avoid hardcoding any absolute paths in the templates. All the variables currently defined are visible within the userinfo template, allowing it to access [% user.whatever %] to correctly reference the current user in the FOREACH loop.

We've created this separate userinfo template and can assume it generates a nice table showing some interesting information about the current user. When you have simple, self-contained elements like this, it's often a good idea to move them out into separate template files. For one thing, the example is easier to read without large chunks of HTML obstructing the high-level view. A more important benefit is that we can now reuse this component in any other template where we need to display the same table of information about a user.

Now that you're familiar with what templates look like, let's move on to see how we go about processing them.