Hot reload for Starlette app with smart asset updates and HTML injection
Project description
starlette-hot-reload
Hot reload for Starlette templates and static files
Overview
starlette-hot-reload reloads the browser when your Starlette templates or
static assets change. It injects a small client script into HTML responses and
uses Server-Sent Events (SSE) to notify the browser about changes.
Quick Start
Install the package:
uv add starlette-hot-reload
# or
pip install starlette-hot-reload
Enable it in your app lifespan:
from contextlib import asynccontextmanager
from starlette.applications import Starlette
from starlette_hot_reload import hot_reload
@asynccontextmanager
async def lifespan(app: Starlette):
async with hot_reload(app=app, watch_dirs=["templates", "static"]):
yield
app = Starlette(
debug=True,
lifespan=lifespan,
)
Run your app:
uvicorn main:app
What You Get
- Reloads the browser when watched files change.
- Injects the client script into HTML responses automatically.
- Uses SSE, no websocket.
- Composes with Starlette lifespan context managers.
- Adds no extra runtime dependency beyond Starlette.
Basic Usage
Hot reload only runs when debug=True.
app = Starlette(debug=True)
Watch specific directories:
@asynccontextmanager
async def lifespan(app: Starlette):
async with hot_reload(
app=app,
watch_dirs=["templates", "static", "assets"],
):
yield
Use a custom SSE endpoint:
async with hot_reload(
app=app,
watch_dirs=["templates", "static"],
events_path="/custom-events-path",
):
...
Tune filesystem polling:
async with hot_reload(
app=app,
watch_dirs=["templates", "static"],
poll_interval=0.25,
):
...
Compose it with other lifespan resources using AsyncExitStack:
from contextlib import AsyncExitStack, asynccontextmanager
@asynccontextmanager
async def lifespan(app: Starlette):
async with AsyncExitStack() as stack:
await stack.enter_async_context(
hot_reload(app=app, watch_dirs=["templates", "static"])
)
yield
How It Works
When you request an HTML page, the middleware injects a small client script into the response body. That script opens an SSE connection to the app.
The file watcher polls the directories you configured. When a file changes:
- Watched changes trigger a full page reload.
This package complements server reload tools such as uvicorn --reload. It does
not restart your Python process.
Limits
- Hot reload is disabled when
debug=False. - The watcher uses polling, not native OS file notifications.
- Python code changes reload the browser page, but they do not reload server state or restart the app process.
- Script injection targets HTML responses. Non-HTML responses are left alone.
Examples
The repository includes example apps under examples/.
examples/basic
This example shows the smallest Starlette setup with:
- Jinja templates
- Static files
hot_reload()in the app lifespan
Run it from the repository root:
uv run python -m examples.basic.app
Open http://127.0.0.1:3000 and edit files in examples/basic/templates or
examples/basic/static.
examples/with_tailwind
This example shows how to compose starlette-hot-reload with
starlette-tailwindcss.
It watches the whole example directory so changes to templates, CSS, and built assets all trigger a browser reload.
Run it from the repository root:
uv run python -m examples.with_tailwind.app
Open http://127.0.0.1:3000 and edit files in examples/with_tailwind/.
Debug Logging
If you want watcher and SSE logs during development, enable Python logging:
import logging
import sys
logging.basicConfig(
level=logging.DEBUG,
format="%(asctime)s %(levelname)s [%(name)s] %(message)s",
datefmt="%Y-%m-%d %H:%M:%S",
stream=sys.stderr,
)
More Starlette Packages
If you are building server-rendered Starlette apps, these packages fit well with
starlette-hot-reload:
- starlette-tailwindcss: Tailwind CSS integration for Starlette apps
- starlette-html-stories:
Storybook-like development tools for
starlette-htmlcomponents - starlette-html: Python-first HTML DSL for server-rendered UI in Starlette
License
MIT
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file starlette_hot_reload-0.3.0.tar.gz.
File metadata
- Download URL: starlette_hot_reload-0.3.0.tar.gz
- Upload date:
- Size: 9.9 kB
- Tags: Source
- Uploaded using 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}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
22e8135a3bfa772aa7e78fb9e58d55dd27449ceed9fa3eb3723c09db701d523e
|
|
| MD5 |
99ec725437fd31b6455a3172ef8bd6c7
|
|
| BLAKE2b-256 |
579fee3ea1f006a64d2ec1a7315573d57fdda01815bb3ba2c7d2a437b0426260
|
File details
Details for the file starlette_hot_reload-0.3.0-py3-none-any.whl.
File metadata
- Download URL: starlette_hot_reload-0.3.0-py3-none-any.whl
- Upload date:
- Size: 12.6 kB
- Tags: Python 3
- Uploaded using 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}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
b4b5cfb8a51d12b4186da0f2ef5820ed0b2a4afdc1d13673186eb37752c78172
|
|
| MD5 |
20c889d22766f08e3d81dcc027a4b3de
|
|
| BLAKE2b-256 |
cf10dd345d1f5ee69a4369eaca762c43b8e54703ec73aed64824da960976dd67
|