Zope Page Templates
Zope's attribute-based HTML/XML template language, made up of TAL, TALES and METAL, which keeps templates valid markup that designers can open in ordinary web editors.
Created by Digital Creations (later Zope Corporation): Todd Coram, Evan Simpson, Guido van Rossum and the ZPT project team
Zope Page Templates (ZPT) is the HTML and XML templating technology of the Zope application server. It is really three small languages used together. The Template Attribute Language (TAL) replaces, repeats or removes parts of a document. TALES, the TAL Expression Syntax, supplies the data. METAL, the Macro Expansion Template Attribute Language, lets templates share reusable pieces. All template logic lives in namespaced XML attributes such as tal:content or tal:repeat, so a page template is still a well-formed HTML or XML document. A designer can open it in a visual web editor and see sample content where the dynamic data will go. ZPT was developed at Digital Creations (later Zope Corporation) in early 2001 as an alternative to Zope’s older DTML. It is still maintained today in Zope, in the standalone zope.pagetemplate packages, and in the faster Chameleon reimplementation that Plone uses.
A note on the date. Some language catalogues list ZPT as a 1999 language. The primary record points to 2001. The first TAL prototype code was committed in January 2001, the first public prototype appeared on zope.org on 16 February 2001, and TAL and PageTemplates 1.0.0 were released on 30 March 2001. No trace of the design before late 2000 or early 2001 has turned up in the Zope source history or in archived zope.org pages. 1999 was when Zope 2.0 appeared, still templated with DTML.
History & Origins
The problem with DTML
Before ZPT, Zope pages were written in DTML (Document Template Markup Language), a tag-based language whose <dtml-...> tags were mixed into HTML. The project’s vision statement set out the problem. Presentation designers “like to work with sophisticated visually oriented web page tools”, but DTML “uses its own non-HTML markup which is counter to the way most web page editors work.” Content, presentation and logic were “all blended into one place.”
The ZPT project wiki proposed a fix: “Adopt a consistent, XML compliant, markup language as a target, embed all logic in namespaced attributes (where they are ignored by the web page tools) and provide a means to supply prototypical content.” The goal was to “establish website production harmony through the seamless integration of the three separate domains: Presentation, Content and Logic.”
From prototype to 1.0 in about two months
Early 2001 moved quickly, and the record is unusually well preserved:
- 26 January 2001: Guido van Rossum, then at Digital Creations through PythonLabs, commits a “Prototype TAL implementation” to the Zope repository. Over the next few days he adds macro expansion, slots, a TAL compiler and an interpreter for precompiled TAL programs.
- 16 February 2001: Zope Presentation Templates 0.1a and 0.2a are posted on zope.org by Todd Coram (
tcoram). They are described as a prototype “primarily for experimentation and feedback” and require the ParsedXML product. The project’s status page adds: “We are looking for feedback. We are in need of examples.” - 22 February 2001: the status page reports that “a core team has been assembled.” The plan is to solidify the specification, then “Release 1.0 and optimize until performance is on par with DTML.”
- 23 March 2001: Evan Simpson makes the “Initial checkin as PageTemplates.” By 27 March the wiki’s specification pages have been reorganized around the TAL, TALES and METAL trio.
- 27 March 2001: TAL and PageTemplates 1.0.0b1 (“All functionality described in the Project Wiki is implemented”).
- 30 March 2001: TAL and PageTemplates 1.0.0.
The project wiki also lists a “Presentation Templates” talk given at the 9th International Python Conference, which it describes as “a bit out of date” by then.
Growing up inside Zope
Releases came every few weeks through 2001. Version 1.2.0 (27 April) added the ZTUtils batching and tree-widget helpers. Version 1.3.0 (21 May) added the default, user and attrs built-in variables and the | fallback operator for paths. Version 1.4.0 (13 August) added omit-tag and allowed TAL and METAL to be used as element names. Page Templates were part of Zope 2.5.0, released on 25 January 2002. Its example applications were described as using “new features including ZPT (including macros) and sessions.”
Design Philosophy
The vision statement lists the principles the team set for itself:
- “Everything is explicit. There are no implicit contexts or magic side effects.”
- It separates presentation from content and business logic.
- It works with plain old HTML as well as XML, and is XML-compliant.
- It allows example content in templates “to allow designers to see/edit their work in context”. The document credits this idea to HiperDOM.
- It is a macro-based template facility for reusable, parameterized chunks of presentation.
- “Things that are not Zope specific shouldn’t be made Zope specific.” The languages were specified abstractly, and the team said it “anticipate[d] implementations of these languages other than the implementation in Zope.”
The FAQ explains why ZPT uses attributes and not new elements. Designers’ HTML editors “will continue to want to deal with HTML, not arbitrary XML,” and new elements “don’t visually render themselves in the editor.” Attributes an editor doesn’t recognize are usually left alone, so a ZPT file can go back and forth between a designer’s tool and the server.
Key Features
TAL: statements as attributes
TAL statements are attributes in the namespace http://xml.zope.org/namespaces/tal:
| Statement | Effect |
|---|---|
tal:define | Define local (or global) variables |
tal:condition | Include the element only if the expression is true |
tal:repeat | Repeat the element once for each item in a sequence |
tal:content | Replace the element’s contents |
tal:replace | Replace the whole element |
tal:attributes | Set or replace attribute values |
tal:omit-tag | Drop the start and end tags but keep the contents |
tal:on-error | Substitute content if an error occurs inside the element |
When one element carries several statements, the TAL 1.4 specification runs them in a fixed order: define, condition, repeat, content or replace, attributes, omit-tag. content and replace cannot appear together. The specification explains the order: variables usually come first, then the decision about whether to render the element at all, then iteration.
Here is a typical template. The text inside the tags is placeholder content that a designer sees in an editor. The server replaces it at render time:
| |
TALES: expressions
TAL does not define what goes inside its attribute values. That job belongs to TALES, where an optional prefix names the expression type:
path:(the usual default) walks an object path such ashere/titleorrequest/form/id, and supports|alternatives.string:is text with$nameor${path}substitution.not:negates another expression.python:, in Zope and most implementations, evaluates a Python expression, for examplepython:'http://example.com/%s' % obj.id.exists:andnocall:test whether a path exists and fetch an object without calling it.
Page templates provide built-in names including nothing, default, options, repeat and attrs. Zope adds here, container, template, request, user, root and modules. Zope evaluates expressions under its security policy, so what template code can reach depends on the user’s permissions.
METAL: macros and slots
METAL, in the namespace http://xml.zope.org/namespaces/metal, handles reuse. A template defines a macro with metal:define-macro, marks the parts that can be customized with metal:define-slot, and other templates use it with metal:use-macro and metal:fill-slot. The METAL specification points out that “macros are always fully expanded, even in a template’s source text, so that the template appears very similar to its final rendering.”
| |
Zope 3 later added metal:extend-macro, which lets one macro build on another by redefining its slots.
Internationalization
Page templates also support an i18n: namespace (i18n:translate, i18n:name, i18n:attributes) for marking up translatable text. Zope and Plone use it for their message catalogues, and Chameleon implements it too.
Evolution
Zope 3 and standalone packages
The Zope 3 rewrite (first production release November 2004) reimplemented page templates as the zope.pagetemplate, zope.tal and zope.tales packages. In October 2007 they became independent releases on PyPI (version 3.4.0), usable outside the Zope application server. The same packages are still maintained by the Zope Foundation. Recent releases include zope.tal 6.0 and zope.tales 7.0 (both September 2025) and zope.pagetemplate 6.1 (21 November 2025), which requires Python 3.10 or later.
Chameleon
In December 2007 Malthe Borch released z3c.pt, a faster page template engine. It led to Chameleon, whose 1.0 release came on 1 November 2009. Chameleon’s documentation calls page templates “originally a Zope invention” but notes that “the Chameleon compiler and Page Templates engine is an entirely new codebase.” It compiles templates to Python bytecode, needs no Zope environment, and adds features such as ${...} interpolation, whose syntax it says was “taken from Genshi.” Chameleon is now the engine behind Plone’s page templates, is used in Pyramid through pyramid_chameleon, and is used by Zope 4 and later. Its latest release is 4.6.0 (31 December 2024).
Other implementations
Because TAL, TALES and METAL were specified separately from Zope, other people implemented them in many languages. PHPTAL describes itself as “a PHP implementation of ZPT work,” written because “the Zope community came with a refreshing idea named TAL.” Petal (“Perl Template Attribute Language - TAL for Perl!”) is on CPAN, last released as 2.26 in May 2020. According to Wikipedia’s list of TAL implementations, others have been written for Python (SimpleTAL), Java, JavaScript, C#, Go and Common Lisp, though many of these are no longer maintained.
Current Relevance
The “Dormant” label found in some catalogues does not match the evidence. ZPT is a mature, slow-moving technology, but it is not abandoned:
- Zope 6.0 was released on 4 March 2026 and still ships
Products.PageTemplates; 6.1 (April 2026) and 6.2 (August 2026) followed. - zope.pagetemplate, zope.tal and zope.tales all had releases in 2025.
- Chameleon had several releases in 2024, and its GitHub repository was still active in 2026.
- Plone Classic UI still uses page templates as its main HTML generation mechanism.
Outside the Zope/Plone/Pyramid world, ZPT is niche. Most Python web frameworks moved to text-based engines such as Jinja2 and Django templates. Even Roundup’s documentation now shows Jinja2 next to TAL. The idea of natural templates that stay valid, previewable markup has spread further. Wikipedia’s article on TAL lists Java’s Thymeleaf as a similar “natural template” language, but not a TAL implementation.
No official Docker image exists for ZPT. In practice you get it by installing zope.pagetemplate or Chameleon from PyPI into an ordinary Python environment.
Why It Matters
Zope Page Templates were among the earliest widely deployed template languages to insist that a template should itself be a valid document. Almost every template language of its era, including PHP, ASP, JSP and ZPT’s own predecessor DTML, inserted code-like tags into markup. Those files broke visual editors and forced designers and programmers to work around each other. ZPT moved all logic into namespaced attributes, filled templates with placeholder content, and split the work into three clearly specified languages for structure, expressions and reuse. This showed that designers and developers could share the same file. The specification was written to be independent of Zope, which led to implementations in PHP, Perl, Java, JavaScript and elsewhere. A quarter century later, the language still renders pages for Zope, Plone and Pyramid applications.
Timeline
Notable Uses & Legacy
Zope
The Zope application server has shipped Page Templates as a standard way to write HTML views since the 2.5 release in 2002, and its Zope Book still includes chapters and a reference appendix on ZPT
Plone
Plone's documentation calls page templates written in TAL, TALES and METAL 'the primary way to generate HTML output in Plone Classic UI', rendered by the Chameleon engine
Roundup issue tracker
Roundup's web interface templates are written in TAL and METAL (for example a METAL 'icing' page macro); its customization guide now shows TAL and Jinja2 versions side by side
Pyramid web framework
The pyramid_chameleon package provides bindings so Pyramid applications can render Chameleon page templates