--- name: nitro description: Use when working with the Python "nitro" ecosystem - nitro-cli (static site generator), nitro-ui (HTML as Python classes), nitro-dispatch (plugin/hook system), nitro-validator (data validation), or nitro-image (Pillow pipeline). Covers imports, public API, and how the pieces compose. --- # Nitro Python Ecosystem Five small Python libraries that compose into a static-site stack. They are independent on PyPI but designed to fit together. nitro-cli pulls in nitro-ui, nitro-datastore, and nitro-dispatch automatically. nitro-validator and nitro-image are optional add-ons. | Package | Install | Import | Role | |---|---|---|---| | nitro-cli | `pip install nitro-cli` | `nitro` | Static site generator. CLI command is `nitro`. | | nitro-ui | `pip install nitro-ui` | `nitro_ui` | Build HTML with Python classes instead of templates. | | nitro-dispatch | `pip install nitro-dispatch` | `nitro_dispatch` | Plugin/hook system. Powers nitro-cli's plugins. | | nitro-validator | `pip install nitro-validator` | `nitro_validator` | Pipe-string data validation, zero deps. | | nitro-image | `pip install nitro-image` | `nitro_img` | Chainable, lazy Pillow pipeline. Note the import name. | Package name vs import name does not always match. The two that bite you: `nitro-ui` -> `nitro_ui`, `nitro-image` -> `nitro_img`. All five require Python 3.7-3.9+ depending on the package. None require any non-Python services. --- ## nitro-cli (static site generator) CLI tool plus a Python API for configuring builds. Pages are Python files in `src/pages/` that export a `render()` function returning a `Page` object built from nitro-ui elements. ### Project layout ``` my-site/ nitro.config.py # Config(...) instance, see below src/ pages/ # *.py -> *.html (mirrors directory structure) index.py # /index.html about.py # /about.html blog/[slug].py # dynamic route, needs get_paths() components/ # reusable functions returning nitro-ui elements styles/ # *.css -> /assets/styles/ static/ # copied to build root as-is public/ # also copied to build root data/ # *.json / *.yaml, loaded with nitro-datastore plugins/ # local plugin files, auto-discovered build/ # output, gitignored .nitro/cache.json # incremental build hashes, gitignored ``` ### Config ```python # nitro.config.py from nitro import Config config = Config( site_name="My Site", base_url="https://mysite.com", build_dir="build", source_dir="src", renderer={"pretty_print": True, "minify_html": False}, plugins=[], ) ``` The variable must be named `config` and be a `Config` instance. ### Public API ```python from nitro import ( Config, Page, env, ImageConfig, ImageOptimizer, OptimizedImage, Island, IslandConfig, IslandProcessor, ) ``` `env` lazy-loads `.env` (needs `pip install nitro-cli[dotenv]`) and exposes `env.SOMETHING`, `env.is_production()`, `env.is_development()`. ### A page ```python # src/pages/index.py from nitro_ui import HTML, Head, Body, Title, Meta, H1, Paragraph from nitro import Page def render(): return Page( title="My Page", meta={"description": "Page description"}, content=HTML( Head(Meta(charset="UTF-8"), Title("My Page")), Body(H1("Hello"), Paragraph("Welcome.")), ), ) ``` `Page(title, content, meta=None, template=None, draft=False)`. Drafts render in `nitro dev` but are skipped in `nitro build` and excluded from the sitemap. ### Dynamic routes ```python # src/pages/blog/[slug].py def get_paths(): return [{"slug": "hello-world", "title": "Hello"}, {"slug": "intro", "title": "Intro"}] def render(slug, title): ... return Page(title=title, content=...) ``` Multiple params work the same way (`[category]/[slug].py` -> `get_paths()` returns dicts with both keys). ### CLI commands | Command | What it does | |---|---| | `nitro new ` | Scaffold a project. `--no-git`, `--no-install`. | | `nitro init` | Add Nitro to an existing dir. `--force` to overwrite. | | `nitro dev` / `serve` | Dev server with WebSocket live reload. `--port`, `--host`, `--open`, `--no-reload`. | | `nitro build` | Production build. `--clean`, `--force`, `--no-minify`, `--no-optimize`, `--no-fingerprint`, `--no-responsive`, `--no-islands`, `--output dist`. | | `nitro preview` | Serve `build/` locally. `--port`, `--open`. | | `nitro clean` | Remove `build/` and `.nitro/`. `--build`, `--cache`, `--all`, `--dry-run`. | | `nitro routes` | List routes. `--json`. | | `nitro check` | Validate without building. `--no-links`. | | `nitro deploy` | Auto-detect Netlify/Vercel/Cloudflare. `--platform`, `--prod`, `--no-build`. | | `nitro export` | Zip the build. `-o`, `--build-first`. | | `nitro info` | Diagnostics. `--json`. | All commands accept `--verbose` / `-v` and `--debug` (full tracebacks). ### Build cache `nitro build` uses content hashes in `.nitro/cache.json`. Page changes rebuild only that page; component or data file changes rebuild everything; config changes trigger a full rebuild. `--force` bypasses; `nitro clean --cache` clears it. ### Islands (partial hydration) Islands let specific components hydrate on the client while the rest stays static. ```python from nitro import Island island = Island( name="counter", component=Counter, # function returning a nitro-ui element props={"count": 0}, client="visible", # load | idle (default) | visible | media | interaction | none client_only=False, media=None, # CSS media query, only with client="media" ) ``` Renders to a `
...
` with the server-side HTML inside. Register a JS hydrator with `window.__registerIsland("counter", function(props) { ... })` returning a string, or an object with `mount()` / `render()`. Disable processing with `nitro build --no-islands`. ### Image optimisation `ImageConfig` controls responsive image generation in `nitro build`. Defaults: sizes `[320, 640, 768, 1024, 1280, 1920]`, formats `["avif", "webp", "original"]`, quality `{avif: 80, webp: 85, jpeg: 85, png: 85}`. The bundler scans built HTML for `` and rewrites them as `` elements with srcsets. External URLs, data URIs, and images smaller than `min_size` (default 1024 bytes) are skipped. Requires Pillow; AVIF requires `pip install nitro-cli[images]`. ### Plugins (built on nitro-dispatch) ```python # src/plugins/analytics.py from nitro.plugins import NitroPlugin, hook class Plugin(NitroPlugin): name = "analytics" version = "1.0.0" @hook('nitro.post_generate', priority=50) def inject(self, data): data['output'] = data['output'].replace('', '') return data ``` Add `"analytics"` to `config.plugins`. Discovery order: installed packages first, then `src/plugins/.py`. The module must export `Plugin`. Hooks: `nitro.init`, `nitro.pre_generate`, `nitro.post_generate` (`{output: html}`), `nitro.pre_build`, `nitro.post_build`, `nitro.process_data`, `nitro.add_commands`. --- ## nitro-ui (HTML as Python classes) Zero-dependency HTML generation. Every element is a class. Children are positional args, attributes are keyword args. Call `.render()` to get HTML. ```python from nitro_ui import Div, H1, Paragraph Div(H1("Hi"), Paragraph("Hello"), cls="container").render() #

Hi

Hello

``` ### Element name vs HTML tag Most are obvious (`Div`, `H1`, `Header`). The non-obvious ones: | nitro-ui | HTML | nitro-ui | HTML | |---|---|---|---| | `Paragraph` | `

` | `UnorderedList` | `