DEV Community

Introduction to Probo-ui - Write HTML Entirely in Python series

The Problem: Split Workflow in Modern Python Web Frameworks

Modern Python web frameworks force developers into a split workflow: business logic lives in Python files with full IDE support, while presentation logic is exiled to template files that offer none of it.

Template languages like Jinja2 introduce their own syntax for conditionals, loops, and variable access - syntax that your linter cannot check, your type checker cannot verify, and your debugger cannot step through. Every context variable passed across that boundary is a potential KeyError waiting to surface at runtime.

Probo eliminates this divide entirely by making HTML a native Python construct - written, validated, and refactored with the same tools you already use for the rest of your codebase.

What is Probo?

Probo is a Python-first, declarative UI rendering framework. Instead of writing HTML in .html files or using template languages like Jinja2, you write everything in pure Python. No template files. No string concatenation. No f-strings full of angle brackets. Just Python functions and classes that are your HTML.

The Two Flavors of Every Tag

Every HTML tag in Probo comes in two forms:

Flavor Example Returns Use Case
Function (lowercase) div(), h1(), p() Rendered HTML Quick rendering, lightweight
Class (uppercase) DIV(), H1(), P() SSDOM tree node Tree manipulation, streaming
from probo import div, DIV

# Function: returns a string immediately
# return_list=True
html_string = div("Hello World",)
# โ†’ "<div>Hello World</div>"

# Class: returns a tree node, call .render() to get the string
node = DIV("Hello World", Id="main-title")

# Because it's a Node, you can manipulate it dynamically
node.add(div("Subtitle added later!"))
html_string = node.render()
# โ†’ '<div id="main-title">Hello World<div>Subtitle added later!</div></div>'

Note: by adding return_deque=True or return_list=True the output changes from string to respective return type.

Built-in Security

Probo auto-escapes all text content by default via markup_escape. If a user submits <script>alert('xss')</script>, Probo renders it as harmless escaped text - no XSS vulnerabilities out of the box.

from probo import p

# User-submitted content is automatically escaped
user_input = '<script>alert("hacked")</script>'
safe_html = p(user_input)
# โ†’ "<p>&lt;script&gt;alert(&quot;hacked&quot;)&lt;/script&gt;</p>"

What You'll Learn in This Series

By the end of this series, you'll be able to:

  • Build full HTML pages in Python
  • Navigate and manipulate a server-side DOM tree
  • Bind dynamic data without prop-drilling
  • Run a web server with routing, caching, and streaming
  • Build interactive UIs with HTMX - with minimal to no JavaScript required
  • Create reusable, skinnable component systems
  • Deploy with Gunicorn or Uvicorn

Let's get started! ๐Ÿ‘‡

Comments

No comments yet. Start the discussion.