Dev.to WebDev 🛠 Dev 👁 0 📖 4 min read

Introduction to Probo-ui — Write HTML Entirely in Python series Part: 02

Your First Elements — Tag Functions Tag functions are the fastest way to generate HTML in Probo-ui. Import them directly from the probo package: from probo import div, h1, h2, p, span, a, img, button, strong, em, ul

Your First Elements — Tag Functions

Tag functions are the fastest way to generate HTML in Probo-ui. Import them directly from the probo package:

from probo import div, h1, h2, p, span, a, img, button, strong, em, ul, li

Creating Elements

Call the function with content as positional arguments and attributes as keyword arguments:

# Simple heading
h1("Hello World")
# → <h1>Hello World</h1>

# With attributes
h1("Hello World", Class="title", Id="main-title")
# → <h1 class="title" id="main-title">Hello World</h1>

# Link with href
a("Click me", href="https://example.com", target="_blank")
# → <a href="https://example.com" target="_blank">Click me</a>

Nesting Elements

Pass elements inside other elements as positional arguments:

page = div(
    h1("Welcome to Probo"),
    p("This is ", strong("bold"), " and ", em("italic"), " text."),
    ul(
        li("First item"),
        li("Second item"),
        li("Third item"),
    ),
    Class="container"
)

This generates:

<div class="container">
  <h1>Welcome to Probo</h1>
  <p>This is <strong>bold</strong> and <em>italic</em> text.</p>
  <ul>
    <li>First item</li>
    <li>Second item</li>
    <li>Third item</li>
  </ul>
</div>

Self-Closing (Void) Elements

Tags like <img>, <br>, <hr>, and <input> work the same way — they just don't accept children:

from probo import img, br, hr, Input, meta, link

# Image
img(src="/images/logo.png", alt="Company Logo")
# → <img src="/images/logo.png" alt="Company Logo"/>

# Line break
br()
# → <br/>

# Input field
Input(type="email", placeholder="[email protected]", required=True)
# → <input type="email" placeholder="[email protected]" required/>

⚠️ Python Reserved Words

Some HTML tags conflict with Python builtins. Probo renames them with a capital letter:

HTML Tag Probo Name Why?
<input> Input() input is a Python builtin
<del> Del() del is a Python keyword
<map> Map() map is a Python builtin
<object> Object() object is a Python builtin
<filter> (SVG) Filter() filter is a Python builtin
<set> (SVG) Set() set is a Python builtin

return types

list : adding return_list as key word arguments signals the Element engine to render the element as list of strings being opening tag as first and closing tag as last

from probo import img, br, h1, Input

# Image
img(src="/images/logo.png", alt="Company Logo",return_list=True)
# → ['<img src="/images/logo.png" alt="Company Logo"/>']

# Line break
br(return_list=True)
# → ["<br/>"]

# Input field
Input(type="email", placeholder="[email protected]", required=True,return_list=True)
# → ["<input type="email" placeholder="[email protected]" required/>"]

# Simple heading
h1("Hello World",return_list=True)
# → ["<h1>","Hello World","</h1>"]

# With attributes
h1("Hello","World", Class="title", Id="main-title",return_list=True)
# → ['<h1 class="title" id="main-title">',"Hello", "World","</h1>"]

deque : adding return_deque as key word arguments signals the Element engine to render the element as collections.deque of strings being opening tag as first and closing tag as last

from probo import img, br, h1, Input

# Image
img(src="/images/logo.png", alt="Company Logo",return_deque=True)
# → deque(['<img src="/images/logo.png" alt="Company Logo"/>'])

# Line break
br(return_deque=True)
# → deque(["<br/>"])

# Input field
Input(type="email", placeholder="[email protected]", required=True,return_deque=True)
# → deque(["<input type="email" placeholder="[email protected]" required/>"])

# Simple heading
h1("Hello World",return_deque=True)
# → deque(["<h1>","Hello World","</h1>"])

# With attributes
h1("Hello","World", Class="title", Id="main-title",return_deque=True)
# → deque(['<h1 class="title" id="main-title">',"Hello", "World","</h1>"])

Building a Component

from probo import div, h1, h2, p, a, ul, li, img, hr

def welcome_component():
    return div(
        div(
            img(src="/logo.png", alt="Logo"),
            h1("My Awesome App"),
            p("Build beautiful UIs in pure Python."),
            a("Get Started →", href="/docs", Class="btn btn-primary"),
            Class="hero"
        ),
        hr(),
        div(
            h2("Features"),
            ul(
                li("🐍 100% Python — no templates needed"),
                li("🌳 Server-Side DOM tree manipulation"),
                li("⚡ Streaming rendering for huge pages"),
                li("🎨 CSS-in-Python styling system"),
            ),
            Class="features"
        ),
        Class="landing-page"
    )

The Limit of Tag Functions

Tag functions are the bread and butter of Probo-ui for static content. You'll use them everywhere you need blazing-fast, immutable HTML generation.

But because they return raw strings, they are permanently locked in once called. What if you need to build a UI tree, inspect it, find specific elements, and inject new data before sending the HTML to the browser?

For that, we need Server-Side DOM Nodes (the Class flavors like DIV()). And that is exactly what we are covering in upcomming Part 3!

📰 Read the original article on Dev.to WebDev

Originally published by Dev.to WebDev. Aggregated on AIWithGhost for educational purposes — full credit and traffic to the original publisher.