Skip to content

Dazzle Runtime Architecture

How the Dazzle runtime works internally.

Naming note (2026-05-25): Earlier drafts of this doc and adjacent files used "DNR" / "Dazzle Native Runtime" as an internal product name. That label has been retired in favour of plain "the Dazzle runtime" — the framework code lives under src/dazzle/back/ and src/dazzle/ui/. Some autogenerated API-reference pages still carry "DNR" in docstrings; those propagate from source-file docstrings and will be cleaned up incrementally.

Overview

The Dazzle runtime transforms your DSL into working applications through a multi-stage pipeline:

DSL Files (.dsl)
    ↓ parse
AppSpec (Dazzle IR)
    ↓ convert
    ├── BackendSpec ──→ Backend runtime (dazzle/back) ──→ FastAPI App
    └── Fragment tree ──→ UI runtime (dazzle/ui)       ──→ HTML + HTMX

Core Principles

1. Spec-First

BackendSpec and UISpec are the source of truth: - Language-agnostic specifications - Multiple runtimes can consume them - No framework lock-in

2. LLM-First

Specifications are structured for: - Deterministic generation - Easy patching by LLMs - Semantic clarity

3. Framework-Agnostic

Backend and frontend frameworks are outputs, not inputs: - FastAPI is the current backend - Pure JS is the frontend (React optional)

Backend Architecture (dazzle/back)

Layers

BackendSpec (language-agnostic specification)
Runtime Generators
    ├── Model Generator → Pydantic models
    ├── Service Generator → CRUD + custom services
    ├── Route Generator → FastAPI routes
    └── Server → Complete FastAPI application

BackendSpec Structure

BackendSpec:
    name: str
    version: str
    entities: list[EntitySpec]      # Data models
    services: list[ServiceSpec]     # Business logic
    endpoints: list[EndpointSpec]   # HTTP routes
    auth_rules: list[AuthRuleSpec]  # Security

Runtime Generation

The runtime generates:

  1. Pydantic Models from EntitySpec
  2. Field types mapped to Python types
  3. Validators from constraints
  4. Create/Update schemas auto-generated

  5. Services from ServiceSpec

  6. CRUD operations with PostgreSQL persistence
  7. Custom operations with handlers
  8. Business rule enforcement

  9. FastAPI Routes from EndpointSpec

  10. RESTful endpoints
  11. Request/response validation
  12. OpenAPI documentation

Frontend Architecture (dazzle/ui)

Design Decision: Server-Rendered HTMX

Why server-rendered templates over client-side JS: - Server is the single source of truth — no client/server state sync - HTMX declarative attributes replace custom JavaScript - Vanilla JS handles ephemeral UI state (toggles, modals, transitions) - Zero build toolchain — two CDN script tags, no node_modules - LLM-friendly — templates are plain HTML with predictable attributes

Rendering Pipeline

AppSpec (Dazzle IR)
    ↓ converters (surface_converter, workspace_converter)
typed Fragment tree (frozen dataclasses)
    ↓ HTML emission (dazzle.render.html.esc by default, ADR-0023 Pattern A)
HTML + hx-* attributes
    ↓ browser
HTMX swaps partial HTML from server

Post-#1042 (v0.67.92) the framework no longer ships Jinja2. Surfaces declare render: fragment in DSL; rendering goes through the typed Fragment substrate (frozen-dataclass primitive tree, validated against an HTML5 element/attribute allowlist, escaped at the emission boundary by default). The few remaining template paths use either f-strings + dazzle.render.html.esc (Pattern A — framework writes HTML) or string.Template (Pattern B — framework executes user-authored templates). See ADR-0023 for the full rationale.

Technology Stack

Technology Role
HTMX Declarative server interactions (hx-get, hx-post, hx-swap)
Typed Fragments Frozen-dataclass primitive tree for server-rendered HTML — dazzle/ui/runtime/
Dazzle CSS Bundled native stylesheet (/styles/dazzle.css) — tokens + components in @layer order, no third-party CSS framework
dz.js Lightweight client-side state (modals, toggles, transitions)
Alpine.js Local component state (filter bars, popovers, modals) — narrowly scoped per ADR-0022

The workspace runtime no longer loads Tailwind or DaisyUI. Site/marketing pages still consume the legacy CDN tags via site_renderer.get_shared_head_html for back-compat with stat-value / bg-base-* class names — see docs/CSS_MIGRATION_GUIDE.md for the rename map.

Rendering Structure

The UI runtime emits HTML through the typed Fragment primitive tree. The DSL layer is unchanged — surfaces, sections, regions still declare layout; the render: fragment annotation tells the renderer to produce HTML from the typed tree rather than (the now-retired) Jinja2 templates. See docs/typed-fragment-pilot-guide.md for the migration notes.

Fragment Rendering

Fragments are the key interaction primitive. When HTMX fires a request, the server renders a fragment and returns partial HTML:

# In a FastAPI route handler
@app.get("/_dazzle/tasks")
async def list_tasks(request: Request, search: str = ""):
    rows = filter_tasks(search)
    return templates.TemplateResponse(
        "fragments/table_rows.html",
        {"request": request, "table": table_context, "rows": rows}
    )

The browser swaps the fragment into the target element — no full page reload.

Interaction Patterns

Pattern Technology Fragment
Search with debounce HTMX hx-trigger="keyup changed delay:300ms" search_input.html
Inline editing HTMX hx-put + JS toggle state inline_edit.html
Bulk actions JS selection state + HTMX submit bulk_actions.html
Slide-over detail CSS transitions + HTMX content load slide_over.html
Form submission HTMX hx-post with validation fragments form.html + form_errors.html
Pagination HTMX hx-get with page parameter table_pagination.html

Conversion Pipeline

AppSpec → BackendSpec

from dazzle_back.converters import convert_appspec_to_backend

backend_spec = convert_appspec_to_backend(appspec)

Transforms: - EntitySpecBackendEntitySpec with field types - SurfaceSpecServiceSpec + EndpointSpec - Infers CRUD operations from surface modes

AppSpec → UISpec

from dazzle_ui.converters import convert_appspec_to_ui

ui_spec = convert_appspec_to_ui(appspec)

Transforms: - WorkspaceSpecUIWorkspaceSpec with layouts - SurfaceSpecComponentSpec with views - Generates default theme from app metadata

Extensibility

Adding New Runtimes

Backend runtimes implement:

def create_app(spec: BackendSpec) -> Application:
    """Generate application from BackendSpec."""

UI runtimes implement:

def generate(spec: UISpec, output_dir: str) -> list[Path]:
    """Generate UI artifacts from UISpec."""

Custom Components

Register custom components in UISpec:

ComponentSpec(
    name="CustomWidget",
    category="custom",
    props_schema=PropsSchema(fields=[...]),
    view=ElementNode(...)
)

Performance Considerations

Backend

  • PostgreSQL with async drivers (asyncpg)
  • Async route handlers
  • Pydantic validation caching
  • Auto-migration on startup

Frontend

  • No virtual DOM (direct DOM manipulation)
  • Fine-grained signal subscriptions
  • CSS variables for theme switching (no recomputation)
  • Lazy component rendering

Future Directions

  • Mobile runtime: UISpec → React Native / Flutter
  • WebSocket support: Real-time updates in specs