Skip to main content

starlette-html-stories

starlette-html-stories provides development tools for documenting, previewing, and testing starlette-html components in Starlette apps.

It gives you a simple way to:

  • collect component examples in one place
  • document design-system usage next to the component examples
  • render stories as isolated Starlette pages during development
  • pass example data as regular Python arguments
  • add story-local routes for HTMX interactions

The goal is to make your app UI easier to discover, reuse, and keep consistent as it grows.

You might also like my other Starlette packages that I use and maintain:

Package Description
starlette-html Python-first HTML DSL
starlette-hot-reload Hot reload for static files
starlette-tailwindcss Tailwind CSS integration

Installation

uv add starlette-html-stories
# or
pip install starlette-html-stories

Usage

Step 1: Compose html_stories in lifespan

html_stories only activates when app.debug is True.

from contextlib import asynccontextmanager

from starlette.applications import Starlette
from starlette_html_stories import html_stories

@asynccontextmanager
async def lifespan(app: Starlette):
    async with html_stories(
        app=app,
        directory="src/app/ui/stories",
    ):
        yield


app = Starlette(debug=True, routes=[...], lifespan=lifespan)

Then open /__stories__/ in your browser.

Step 2: Write a component story

Stories are plain Python functions.

from app.ui.components import UserCard
from starlette_html_stories import stories, story

stories(title="Design System/UserCard", component=UserCard)

# Shows the default user card state used across the app.
@story(args={"user": {"name": "Ada", "email": "ada@example.com"}})
def Default(args):
    return UserCard(user=args["user"])

The leading comment is used as story documentation. You can also use a function docstring or pass docs= explicitly.

@story(args={"user": {"name": "Grace", "email": "grace@example.com"}})
def GraceHopper(args):
    """Shows a second user fixture for documentation and visual checks."""
    return UserCard(user=args["user"])

Preview Layout

Use preview_layout when stories should use your app shell, global CSS, or design-system layout.

from contextlib import asynccontextmanager
from functools import partial

from app.ui.layouts import BaseLayout
from starlette_html_stories import html_stories


@asynccontextmanager
async def lifespan(app):
    async with html_stories(
        app=app,
        directory="src/app/ui/stories",
        preview_layout=partial(BaseLayout, page_title="Stories"),
    ):
        yield

This keeps each story focused on the component:

@story(args={"user": {"name": "Ada", "email": "ada@example.com"}})
def Default(args):
    return UserCard(user=args["user"])

HTMX-Friendly Stories

Story-local routes make interactive examples possible without wiring a story to your real business logic.

Use ctx.url_for(...) to point HTMX attributes at a route owned by the current story.

@story(routes=[Route("/load-more", load_more, name="load_more")])
def WithLoadMore(ctx):
    return Feed(
        hx_get=ctx.url_for("load_more"),
        hx_target="#feed",
    )

Example App

The repository includes a basic example.

uv run uvicorn examples.basic:app --reload

Open:

  • http://127.0.0.1:8000/
  • http://127.0.0.1:8000/__stories__/

Development

Run the development server when working on starlette-html-stories itself:

just dev

This starts tools/app.py, which uses the dev-only starlette-tailwindcss dependency to build and watch tools/styles.css. The generated CSS is written to src/starlette_html_stories/static/stories.css, ignored by git, and bundled with the published package.

For release, run:

just build
uv publish

License

MIT

Metadata

Release files for starlette-html-stories 0.2.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for starlette-html-stories 0.2.0
File Size Uploaded Provenance Actions
starlette_html_stories-0.2.0.tar.gz 12.6 kB Without provenance Details for starlette_html_stories-0.2.0.tar.gz

Built distribution (wheel)

Table of built distributions (wheels) for starlette-html-stories 0.2.0
File Interpreter ABI Platform Actions
starlette_html_stories-0.2.0-py3-none-any.whl Python 3 none any Details for starlette_html_stories-0.2.0-py3-none-any.whl

Total release size: 28.6 kB

Release files / starlette_html_stories-0.2.0.tar.gz

Download URL starlette_html_stories-0.2.0.tar.gz
Size 12.6 kB
Tags Source
SHA-256 checksum
How to use checksums
e42ad92378ba2a31a423b2ee1f529dde11de25056f4988eb5d71fe0360a3350b
BLAKE2b-256 checksum
How to use checksums
ed29126567fbd94cb4bad4caac1aa0cd7b3d6d8e0249f4c36b5017ea6db36a51
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via uv/0.11.4 {"installer":{"name":"uv","version":"0.11.4","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

Release files / starlette_html_stories-0.2.0-py3-none-any.whl

Download URL starlette_html_stories-0.2.0-py3-none-any.whl
Size 16.0 kB
Tags Python 3
SHA-256 checksum
How to use checksums
ab897d4f7646a0c12b1a88c160c6889c422a94741f35f1ad83d2cc68cbfb37e0
BLAKE2b-256 checksum
How to use checksums
5053006c08ca00490a7c699b9685c1b5b070bfe0fa63417c5d0dd69c46f404c2
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via uv/0.11.4 {"installer":{"name":"uv","version":"0.11.4","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

Security for starlette-html-stories 0.2.0

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

No provenance found

None of the files in this release were uploaded with provenance metadata.

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Release history Release notifications | RSS feed

This release

0.2.0 This release

2 release files No provenance

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page