Build Your Own Template Engine in Python and JavaScript – A Step‑by‑Step Guide

This article walks through the fundamentals of creating a tiny template engine, explaining how templates are split into text, variables and blocks, how delimiters are parsed, how an abstract syntax tree is built, and how both Python and JavaScript can render the template using eval or Function constructors.

21CTO
21CTO
21CTO
Build Your Own Template Engine in Python and JavaScript – A Step‑by‑Step Guide

Template engines are tools that dynamically generate web pages; common examples include PHP's Smarty, Python's Jinja, and Node's Jade. This tutorial builds two simple engines—one in Python and one in JavaScript—to illustrate how to write a template engine from scratch.

Template Structure

A template consists of three parts:

Text – literal strings that are output directly.

Variables – placeholders that are replaced by values from the context.

Blocks – control structures such as conditionals and loops.

Variables and blocks are identified by delimiters, e.g. {{}} for variables and {% … %} for blocks.

Rendering Variables

Rendering a variable requires evaluating the variable name against the provided context. A straightforward way in many languages is to use eval (or a safer variant such as ast.literal_eval in Python). For example:

name = "rainy"
print("Hello, " + eval("name") + "!")  # => Hello, rainy!

In practice, a template engine works like a compiler: the template source is parsed into an abstract syntax tree (AST) and then each node is rendered. The following diagram shows the AST for a simple template:

Template AST diagram
Template AST diagram

Python Implementation

The Python engine first defines token delimiters:

VAR_TOKEN_START = '{{'
VAR_TOKEN_END   = '}}'
BLOCK_TOKEN_START = '{%'
BLOCK_TOKEN_END   = '%}'
TOK_REGEX = re.compile(r"(%s.*?%s|%s.*?%s)" % (VAR_TOKEN_START, VAR_TOKEN_END, BLOCK_TOKEN_START, BLOCK_TOKEN_END))

It then splits the template string, builds an AST list, and renders each node. Variable rendering is handled by a helper:

def resolve(name, context):
    for tok in name.split('.'):  # simple dotted lookup
        context = context[tok]
    return context

A class encapsulates the process:

class VarTmpl:
    def __init__(self, var):
        self.var = var
    def render(self, **kwargs):
        return resolve(self.var, kwargs)

Using the class:

tmpl = VarTmpl("name")
print(tmpl.render(name="rainy"))  # => rainy

JavaScript Implementation

JavaScript offers a similar eval function, but also the more flexible new Function() constructor. A minimal engine defines a function that receives the template string and a context object:

var Tmpl = function(context) {
    with(context) {
        console.log(name);
    }
};
Tmpl({name: "rainy"}); // => rainy

To support blocks, the engine replaces delimiters with JavaScript code, builds a function body, and executes it. The classic micro‑templating code by John Resig follows this pattern:

(function(){
    this.tmpl = function(str, data){
        var fn = new Function('obj',
            "var p=[]; with(obj){p.push('"+
            str.replace(/\t|
|\r/g, " ")
               .replace(/'(?=[^]*?')/g, "\\'")
               .replace(/\{\{(.*?)\}\}/g, "', $1, '")
               .replace(/\{%=(.*?)%\}/g, "', $1, '")
               .replace(/\{%(.+?)%\}/g, "'); $1 p.push('\');
            .split('
').join('');
        return data ? fn(data) : fn;
    };
})();

console.log(tmpl('Hello, {{name}}!', {name: 'rainy'})); // Hello, rainy!

The core idea is the same as the Python version: split the template by delimiters, turn each segment into JavaScript code that pushes strings or evaluates expressions, then join the result.

Summary

The complexity of a template engine lies mainly in building and traversing the AST, but the essential challenge is separating program code from data. Languages like Lisp treat code and data uniformly, which explains why template engines feel natural once the parsing and rendering steps are understood.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

JavaScriptPythonASTTemplate Engineeval
21CTO
Written by

21CTO

21CTO (21CTO.com) offers developers community, training, and services, making it your go‑to learning and service platform.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.