Basic Usage

htm.py advertises itself as a Python implementation of htm, a JavaScript templating package. htm advertises itself as a replacement for JSX, a simpler way to do VDOM-compatible templating.

Let’s take a look at some of the templating patterns in htm.py.

Hello World

Let’s start with generating <div>Hello World</div>. This is a single node (the <div>) with one child, a text node (Hello World.)

Here is a “template”, the html function, which generates the VDOM-like output:

from htm import htm


@htm
def html(tag, props, children):
    return tag, props, children


result01 = html("""
  <div>Hello World</div>
""")

What is this VDOM-like output? A series of possibly-nested tuples, as explained in TODO.

>>> result01
('div', {}, ['Hello World'])

Dynamic Values

That was pretty boring…all static, no “templating”. Let’s have the template insert a value:

name = 'World'

result02 = html("""
  <div>Hello {name}</div>
""")

As you can see, we use brackets, as in {name}, to insert values. The output is just a little be different now:

>>> result02
('div', {}, ['Hello ', 'World'])

Static Attributes

Wonder what the second item – the empty {} – is for in the tuple? That’s where attributes for that node would go. Let’s add an attribute:

name = 'World'

result03 = html("""
  <div title="Say Hi">Hello {name}</div>
""")

This returns:

>>> result03
('div', {'title': 'Say Hi'}, ['Hello ', 'World'])

So now you see the structure of the tuple:

  • First item is the tag name

  • Second item is for attribute information

  • Third item is for child nodes

Dynamic Attributes

Attributes aren’t always static, of course. You can make them dynamic by – you guessed it – curly brackets containing an expression:

message = 'Say Howdy'
name = 'World'

result04 = html("""
  <div title="{message}">Hello {name}</div>
""")

And the result:

>>> result04
('div', {'title': 'Say Howdy'}, ['Hello ', 'World'])

Nested Children

We’re dealing with a single node. But what happens when we have a tree of nodes, which is usually the case? That’s where nesting comes in, and that’s where the VDOM approach shines.

message = 'Say Howdy'
name = 'World'

result05 = html("""
  <section>
    <div title="{message}">Hello {name}</div>
  </section>
""")

The result now has a tuple whose third item – the children – is a sequence:

>>> result05
('section', {}, [('div', {'title': 'Say Howdy'}, ['Hello ', 'World'])])

Inline Python

Perhaps we want the name value in all uppercase? This “template” language supports Python expressions inside the brackets:

message = 'Say Howdy'
name = 'World'

result06 = html("""
  <section>
    <div title="{message}">Hello {name.upper()}</div>
  </section>
""")

The output is the same, but with World in all caps:

>>> result06
('section', {}, [('div', {'title': 'Say Howdy'}, ['Hello ', 'WORLD'])])

Looping

Rendering a list of things is very common. JSX, Jinja2, and most other template-like environments make it easy to do so. In JSX, you use the language’s looping facilities. Same in htm.py:

message = 'Say Howdy'
names = ['World', 'Universe', 'Galaxy']

result07 = html("""
  <ul title="{message}">
    {[
        html('<li>Hello {name}</li>')
        for name in names
     ] }
  </li>
""")

What’s the output? The children of ul – meaning, the third element of the tuple – holds a sequence of sequences:

>>> result07
('ul', {'title': 'Say Howdy'}, [[('li', {}, ['Hello ', 'World']), ('li', {}, ['Hello ', 'Universe']), ('li', {}, ['Hello ', 'Galaxy'])]])

Conditional Rendering

Maybe you only want a footer when a value is provided:

from htm import htm


@htm
def html(tag, props, children):
    return tag, props, children


# start
message = 'Say Howdy'
not_message = 'So Sad'
show_message = True

result08 = html("""
    <h1>Show?</h1>
    {message if show_message else not_message}
""")

Our output now contains the children of the caller, placed in the spot determined by the subcomponent:

>>> result08
[('h1', {}, ['Show?']), 'Say Howdy']

Here’s another variation that inserts a subtemplate conditionally:

from htm import htm


@htm
def html(tag, props, children):
    return tag, props, children


# start
message = 'Say Howdy'
not_message = 'So Sad'
show_message = True

result08b = html("""
  <div>
    <h1>Show?</h1>
    {html('''<p>{message}</p>''') if show_message else html('''<p>{not_message}</p>''')}
  </div>
""")

We now get some richer output:

>>> result08b
('div', {}, [('h1', {}, ['Show?']), ('p', {}, ['Say Howdy'])])

Subcomponents

In React and JSX, you frequently split things into lots – LOTS – of small, single-focused components. Let’s extract the <li> into a subcomponent:

message = 'Say Howdy'
names = ['World', 'Universe', 'Galaxy']


def greeting(name):
    return html('<li>Hello {name}</li>')


result09 = html("""
  <ul title="{message}">
    {[greeting(name) for name in names]}
  </li>
""")

The results are the same:

>>> result09
('ul', {'title': 'Say Howdy'}, [[('li', {}, ['Hello ', 'World']), ('li', {}, ['Hello ', 'Universe']), ('li', {}, ['Hello ', 'Galaxy'])]])