Build static sites with Python.

Templates are a second language to learn and debug. A Nitro page is a Python function that returns HTML, and the build is a folder you can host anywhere.

$ pip install nitro-cli
Quickstart

Latestv1.0.17
PackagesSix
RequiresPython 3.9+
TemplatesNone
You writepage.py
from nitro_ui import Body, Div, H1, H2, Paragraph, Section
from nitro import Page

FEATURES = [
    ("No templates", "Pages are Python functions."),
    ("No config", "Sensible defaults, zero setup."),
]

def render():
    return Page(
        title="Hello",
        content=Body(
            H1("Hello, World!"),
            Section(
                *[
                    Div(H2(name), Paragraph(text), cls="card")
                    for name, text in FEATURES
                ],
                cls="features",
            ),
        ),
    )
Nitro buildsindex.html
<!doctype html>
<html>
  <body>
    <h1>Hello, World!</h1>
    <section class="features">
      <div class="card">
        <h2>No templates</h2>
        <p>Pages are Python functions.</p>
      </div>
      <div class="card">
        <h2>No config</h2>
        <p>Sensible defaults, zero setup.</p>
      </div>
    </section>
  </body>
</html>
No template language in between.
The model

Three concepts.

No build config and no magic. If you can write a Python function, you can write a page.

page.py
def render():
    return Page(
        title="Hello",
        content=Body(H1("Hello, World!")),
    )
1 / 3

Pages are functions.

Loops, conditionals, imports, f-strings: everything Python already does, instead of a template dialect that reimplements half of it.

component.py
Card(
    H2("Welcome"),
    Paragraph("Get started in seconds."),
    cls="hero-card"
)
2 / 3

Components are classes.

Nest them, reuse them, pass data as keyword arguments. A misspelled element is a NameError at build time, not a silently empty div in production.

data.py
data = DataStore.from_file("posts.json")

for post in data.posts:
    Card(title=post.title)
3 / 3

Data is dot notation.

Point it at a JSON file and read data.posts straight off it. No schema to define first, and no chain of dictionary keys to get wrong.

The CLI

Scaffold, watch, build.

Live reload while you work, incremental builds that skip what hasn't changed, and a plain folder of HTML at the end.

Terminal~/my-site
Commandnitro dev
Port3000
ReloadLive
OutputStatic HTML

Python is all you need.

No webpack. No template languages. No JavaScript required. Just Python functions that return HTML.

Read the docs