Python Web Utilities

Nitro

A collection of Python web-dev utils inspired by smart solutions across the ecosystem. Use them together or separately, with your favourite framework.

Pure PythonNo build stepFramework agnostic
Architecture

Five Layers, One Philosophy

Each layer solves one problem. Use what you need, ignore the rest.

0
TemplatingPage layouts, headers, decorators
1
HTML GenerationRust-powered Python → HTML
2
ReactivitySignals, SSE, DOM morphing
3
Rich EntitiesActive Record + persistence
4
Event RoutingBlinker signals + decorators
0

Templating

Python functions that return HTML. Powered by Datastar for interactivity.

templating.py
from nitro.html.components import *  # noqa: F403
from nitro.html import template as templ
from your.components import Sidebar, Navbar

# Shared page template
htmlkws = dict(lang="en")
page = page_template(htmlkw=htmlkws, lucide=True)


@templ
def template(content, title: str):
    return page(
        Fragment(
            Sidebar(),
            Main(
                Navbar(),
                Div(
                    Div(content, id="content"),
                    cls="p-4 md:p-6 xl:p-12",
                ),
            ),
        ),
        title=title,
    )
my_page.py
from templates import template

@template
def index():
    return Div(
        H1("Hello, World!"),
        P("This is a template."),
    )
1

HTML Generation

Rust-powered speed. Python simplicity. Inspired by FastHTML.

Output

What the browser sees

Jane Doe

Software Engineer

Building things with Nitro. It's just Python. That's the whole pitch.

PythonFastAPINo JS
user_card.py
from rusty_tags import Div, H3, P
from nitro.html.components import Avatar, Badge

def UserCard(user):
    return Div(
        Div(
            Avatar(fallback=user.initials, size="lg"),
            Div(
                H3(user.name, cls="font-semibold"),
                P(user.role, cls="text-sm text-muted-foreground"),
            ),
            cls="flex items-center"
        ),
        P(user.bio, cls="mt-4"),
        Div(
            *[Badge(tag) for tag in user.tags],
            cls="flex gap-2 mt-4"
        ),
    )

# That's it. No JSX. No templates. Just Python.
2

Reactivity

Signals powered by Datastar. 14kb. Does what React does. No npm install.

counter.py
from rusty_tags import Div, Button, Span
from rusty_tags.datastar import Signals

def Counter():
    sigs = Signals(count=0)

    return Div(
        Button("-", on_click=sigs.count.sub(1)),
        Span(data_text=sigs.count, cls="text-4xl font-bold mx-8"),
        Button("+", on_click=sigs.count.add(1)),
        data_signals=sigs,
    )

# Datastar handles the reactivity.
# No virtual DOM. No hydration. No build step.

Live Counter

State lives in HTML. UI updates automatically.

Click the buttons. The count updates. No useState, no Redux, no tears.

3

Rich Entities

Business logic in methods. Persistence is automatic. Built on SQLModel.

Order #1234

Created 2 minutes ago

placed
CustomerAcme Corp
Total$1,234.00
Items3 products
order.py
from nitro.domain.entities.base_entity import Entity
from pydantic import Field

class Order(Entity, table=True):
    customer: str = Field(title="Customer")
    total: float = Field(default=0.0, title="Total")
    status: str = Field(default="draft", title="Status")

    def add_item(self, product, qty):
        self.total += product.price * qty
        self.save()  # Just works

    def place(self):
        if self.total == 0:
            raise ValueError("Empty order")
        self.status = "placed"
        self.save()

# order = Order(customer="Acme Corp")
# order.add_item(widget, 3)
# order.place()
#
# That's your business logic. In one place.
4

Event Routing

Decoupled handlers. Built on Blinker with async support and priority ordering.

handlers.py
from nitro.events import subscribe, publish_sync

# Handlers are just decorated functions

@subscribe("order.placed")
async def send_confirmation(msg):
    EmailService.send(
        msg.data["customer_email"],
        "Your order is confirmed!"
    )

@subscribe("order.placed")
async def update_inventory(msg):
    for item in msg.data["items"]:
        Product.get(item["id"]).reduce_stock(item["qty"])

@subscribe("order.*")
async def notify_warehouse(msg):
    WarehouseQueue.push(msg.data["id"])

# Publish from anywhere — subscribers fire independently
publish_sync("order.placed", data=order.model_dump())

# Decoupled. Testable. No spaghetti.

Event Flow

What happens when an order is placed

order.place()

Fires 'order-placed' event

send_confirmation()
update_inventory()
notify_warehouse()

Model Views

Pass your Entity. Get forms, tables, cards. Field metadata controls everything.

Auto-Generated UI

One Entity, multiple views

class User(Entity)

Define once with Field metadata

ModelTable(User, data=users)
ModelCard(User, instance=user)
ModelForm(User)
views.py
from nitro.domain.entities.base_entity import Entity
from nitro.html.components.model_views import (
    ModelForm, ModelTable, ModelCard
)
from pydantic import Field

class User(Entity, table=True):
    name: str = Field(title="Name")
    email: str = Field(
        title="Email",
        json_schema_extra={'format': 'email'}
    )
    role: str = Field(
        default="member",
        json_schema_extra={'icon': 'user'}
    )

# Generate a complete form
form = ModelForm(User)

# Generate a data table with sorting
table = ModelTable(User, data=User.all())

# Generate a display card
card = ModelCard(User, instance=user)

# That's < 20 lines for a complete CRUD interface.
Quick start

Up and Running in 60 Seconds

Three commands. No configuration files. No boilerplate.

1
Installpip install nitro-boost
2
Createnitro boost
3
Runpython app.py

That's It

Five layers. Pure Python. No magic, just patterns that work.

Use it if it helps. Fork it if you want. File issues if it breaks.