Advanced Usage

This document covers some of staticjinja’s more advanced features.

Partials and ignored files

A partial file is a file whose name begins with a _. Partial files are intended to be included in other files and are not rendered. If a partial file changes, it will trigger a rebuild if you are running staticjinja watch.

An ignored file is a file whose name begins with a .. Ignored files are neither rendered nor used in rendering templates.

If you want to configure what is considered a partial or ignored file, subclass Site and override is_partial or is_ignored.

Using Custom Build Scripts

The command line shortcut is convenient, but sometimes your project needs something different than the defaults. To change them, you can use a build script.

A minimal build script looks something like this:

from staticjinja import Site

if __name__ == "__main__":
    site = Site.make_site()
    # enable automatic reloading

To change behavior, pass the appropriate keyword arguments to Site.make_site.

  • To change which directory to search for templates, set searchpath="searchpath_name" (default is ./templates).
  • To change the output directory, pass in outpath="output_dir" (default is .).
  • To add Jinja extensions, pass in extensions=[extension1, extension2, ...].
  • To change which files are considered templates, subclass the Site object and override is_template.
  • To change where static files (such as CSS or JavaScript) are stored, set staticpaths=["mystaticfiles/"] (the default is None, which means no files are considered to be static files). You can pass multiple directories in the list: staticpaths=["foo/", "bar/"]. You can also specify singly files to be considered as static: staticpaths=["favicon.ico"].

Finally, just save the script as (or something similar) and run it with your Python interpreter.

$ python
Building index.html...
Templates built.
Watching 'templates' for changes...
Press Ctrl+C to stop.

Loading data

Some applications render templates based on data sources (e.g. CSVs or JSON files).

The simplest way to supply data to templates is to pass Site.make_site() a mapping from variable names to their values (a “context”) as the env_globals keyword argument.

if __name__ == "__main__":
    site = Site.make_site(env_globals={
        'greeting':'Hello world!',

Anything added to this dictionary will be available in all templates:

<!-- templates/index.html -->

If the context needs to be different for each template, you can restrict contexts to certain templates by supplying Site.make_site() a sequence of regex-context pairs as the contexts keyword argument. When rendering a template, staticjinja will search this sequence for the first regex that matches the template’s name, and use that context to interpolate variables. For example, the following code block supplies a context to the template named “index.html”:

from staticjinja import Site

if __name__ == "__main__":
    context = {'knights': ['sir arthur', 'sir lancelot', 'sir galahad']}
    site = Site.make_site(contexts=[('index.html', context)])
<!-- templates/index.html -->
<h1>Knights of the Round Table</h1>
{% for knight in knights %}
    <li>{{ knight }}</li>
{% endfor %}

If contexts needs to be generated dynamically, you can associate filenames with functions that return a context (“context generators”). Context generators may either take no arguments or the current template as its sole argument. For example, the following code creates a context with the last modification time of the template file for any templates with an HTML extension:

import datetime
import os

from staticjinja import Site

def date(template):
    template_mtime = os.path.getmtime(template.filename)
    date = datetime.datetime.fromtimestamp(template_mtime)
    return {'template_date': date.strftime('%d %B %Y')}

if __name__ == "__main__":
    site = Site.make_site(
        contexts=[('.*.html', date)],

By default, staticjinja uses the context of the first matching regex if multiple regexes match the name of a template. You can change this so that staticjinja combines the contexts by passing mergecontexts=True as an argument to Site.make_site(). Note the order is still important if several matching regex define the same key, in which case the last regex wins. For example, given a build script that looks like the following code block, the context of the index.html template will be {'title': 'MySite - Index', 'date': '05 January 2016'}.

import datetime
import os

from staticjinja import Site

def base(template):
    template_mtime = os.path.getmtime(template.filename)
    date = datetime.datetime.fromtimestamp(template_mtime)
    return {
        'template_date': date.strftime('%d %B %Y'),
        'title': 'MySite',

def index(template):
    return {'title': 'MySite - Index'}

if __name__ == "__main__":
    site = Site.make_site(
        contexts=[('.*.html', base), ('index.html', index)],


Filters modify variables. staticjinja uses Jinja2 to process templates, so all the standard Jinja2 filters are supported. To add your own filters, simply pass filters as an argument to Site.make_site().

filters = {
    'hello_world': lambda x: 'Hello world!',
    'my_lower': lambda x: x.lower(),

if __name__ == "__main__":
    site = Site.make_site(filters=filters)

Then you can use them in your templates as you would expect:

<!-- templates/index.html -->
{% extends "_base.html" %}
{% block body %}
<h1>{{'' | hello_world}}</h1>
<p>{{'THIS IS AN EXAMPLE WEB PAGE.' | my_lower}}</p>
{% endblock %}

Compilation rules

Sometimes you’ll find yourself needing to change how a template is compiled. For instance, you might want to compile files with a .md extension as Markdown, without needing to put jinja syntax in your Markdown files.

To do this, just write a handler by registering a regex for the files you want to handle, and a compilation function (a “rule”).

import os

from staticjinja import Site

# Custom MarkdownExtension
from extensions import MarkdownExtension

def get_post_contents(template):
    with open(template.filename) as f:
        return {'post':}

# compilation rule
def render_post(env, template, **kwargs):
    """Render a template as a post."""
    post_template = env.get_template("_post.html")
    head, tail = os.path.split(
    post_title, _ = tail.split('.')
    if head:
        out = "%s/%s.html" % (head, post_title)
        if not os.path.exists(head):
        out = "%s.html" % (post_title, )**kwargs).dump(out)

if __name__ == "__main__":
    site = Site.make_site(extensions=[
    ], contexts=[
        ('.*.md', get_post_contents),
    ], rules=[
        ('.*.md', render_post),

Note the rule we defined at the bottom. It tells staticjinja to check if the filename matches the .*.md regex, and if it does, to compile the file using render_post.

Now just implement templates/_post.html

<!-- templates/_post.html -->
{% extends "_base.html" %}
{% block content %}
<div class="post">
{% markdown %}
{{ post }}
{% endmarkdown %}
{% endblock %}

This would allow you to drop Markdown files into your templates directory and have them compiled into HTML.


You can grab MarkdownExtension from