A static site generator that lets you build websites using Python and nitro-ui.
Features
- Python-Powered - Write pages in Python with nitro-ui instead of template languages
- Live Reload - Development server with automatic browser refresh
- Incremental Builds - Only rebuild changed pages
- Dynamic Routes - Generate pages from data with
[slug].pypattern - Draft Pages - Mark pages as drafts to exclude from production builds
- Environment Variables - Auto-load
.envfiles withfrom nitro import env - Image Optimization - Responsive images with WebP/AVIF conversion
- Islands Architecture - Partial hydration for interactive components
- Plugin System - Extend the build lifecycle with nitro-dispatch hooks
- One-Click Deploy - Netlify, Vercel, or Cloudflare Pages
Installation
pip install nitro-cli
AI Assistant Integration
Add Nitro CLI knowledge to your AI coding assistant:
npx skills add nitro-sh/nitro-cli
This enables AI assistants like Claude Code to understand Nitro CLI and generate correct nitro-ui code.
Quick Start
nitro new my-site
cd my-site
nitro dev
Visit http://localhost:3000. Build for production with nitro build.
Writing Pages
Pages are Python files in src/pages/ that export a render() function:
# src/pages/index.py
from nitro_ui import HTML, Head, Body, Title, Meta, H1
from nitro import Page
def render():
return Page(
title="Home",
content=HTML(
Head(
Meta(charset="UTF-8"),
Meta(name="viewport", content="width=device-width, initial-scale=1.0"),
Title("Home"),
),
Body(H1("Welcome!"))
)
)
Output paths mirror the file structure: src/pages/about.py → build/about.html
Dynamic Routes
Generate multiple pages from data using [param].py naming:
# src/pages/blog/[slug].py
from nitro import Page
from nitro_datastore import NitroDataStore
def get_paths():
data = NitroDataStore.from_file("src/data/posts.json")
return [{"slug": p.slug, "title": p.title} for p in data.posts]
def render(slug, title):
return Page(title=title, content=...)
Commands
| Command | Description |
|---|---|
nitro new <name> | Create new project |
nitro init | Initialize Nitro in current dir |
nitro dev | Start dev server with live reload |
nitro build | Build for production |
nitro preview | Preview production build |
nitro routes | List all routes |
nitro check | Validate site without building |
nitro export | Export site as zip archive |
nitro clean | Remove build artifacts |
nitro deploy | Deploy to hosting platform |
nitro info | Show project and environment info |
Run nitro <command> --help for options.
Configuration
# nitro.config.py
from nitro import Config
config = Config(
site_name="My Site",
base_url="https://mysite.com",
renderer={"minify_html": True},
plugins=[],
)
Ecosystem
- nitro-ui - Build HTML with Python, not strings
- nitro-datastore - Schema-free JSON data store with dot notation access
- nitro-dispatch - Framework-agnostic plugin system
- nitro-image - Fast, friendly image processing for the web
- nitro-validate - Dependency-free data validation
License
This project is licensed under the BSD 3-Clause License. See the LICENSE file for details.
API Reference
Auto-generated from the installed package's public API. Signatures and docstrings come directly from the source.
Page
Page#
Page(title: str, content: Any, meta: Optional[Dict[str, Any]] = None, template: Optional[str] = None, draft: bool = False)A single rendered page in a Nitro site.
Config
Config#
Config(site_name: str = 'My Site (built with nitro.sh)', base_url: str = 'http://localhost:8008', build_dir: str = 'build', source_dir: str = 'src', renderer: Optional[Dict[str, Any]] = None, plugins: Optional[List[str]] = ...Project-level configuration for a Nitro site.
Environment
env#
Lazy-loading accessor for environment variables.
Image pipeline
ImageConfig#
ImageConfig(sizes: List[int] = <factory>, formats: List[str] = <factory>, quality: Dict[str, int] = <factory>, lazy_load: bool = True, default_sizes: str = '(max-width: 640px) 100vw, (max-width: 1024px) 50vw, 33vw', output_dir: ...Tunable settings for the image optimization pipeline.
ImageOptimizer#
ImageOptimizer(config: Optional[nitro.core.images.ImageConfig] = None)Build-time image optimizer that produces responsive variants. Build a responsive `<picture>` element for an optimized image. Generate responsive variants for a single source image. Rewrite `<img>` tags in HTML into optimized `<picture>` elements.3 methods
generate_picture_element(self, optimized: nitro.core.images.OptimizedImage, alt: str = '', css_class: str = '', sizes: Optional[str] = None) -> stroptimize_image(self, source_path: pathlib.Path, output_dir: pathlib.Path, base_url: str = '') -> Optional[nitro.core.images.OptimizedImage]process_html(self, html_content: str, source_dir: pathlib.Path, output_dir: pathlib.Path, base_url: str = '') -> str
OptimizedImage#
OptimizedImage(original_path: pathlib.Path, original_width: int, original_height: int, variants: Dict[str, Dict[int, pathlib.Path]], hash: str) -> NoneResult of optimizing a single source image. Return a single `src` path for the given format and width. Return a `srcset` attribute value for the given format.2 methods
get_src(self, format: str = 'webp', width: Optional[int] = None) -> strget_srcset(self, format: str = 'webp') -> str
Islands
Island#
Island(name: str, component: Any, props: Dict[str, Any] = <factory>, client: Literal['load', 'idle', 'visible', 'media', 'interaction', 'none'] = 'idle', client_only: bool = False, media: Optional[str] = None) -> NoneAn interactive component that hydrates on the client. Render the island as HTML with hydration markers.1 methods
render(self) -> str
IslandConfig#
IslandConfig(output_dir: str = '_islands', default_strategy: Literal['load', 'idle', 'visible', 'media', 'interaction', 'none'] = 'idle', debug: bool = False) -> NoneTunable settings for the islands hydration pipeline.
IslandProcessor#
IslandProcessor(config: Optional[nitro.core.islands.IslandConfig] = None)Post-process HTML to wire up island hydration. Return the client-side hydration runtime as a JS source string. Inject the hydration runtime into HTML when islands are present.2 methods
generate_hydration_script(self) -> strprocess_html(self, html_content: str, inject_script: bool = True) -> str