Class-based encapsulation
Layout, callbacks, state, and IDs live together in one class. Instantiate twice for two independent copies that don't step on each other.
Components →Auto-unique IDs
The Identifier() descriptor generates a unique Dash ID per instance. No more managing string IDs by hand or hitting DuplicateIdError.
Multi-page routing
SimpleRouterComponent maps URL paths to component instances. Built-in 404 handling, optional auth gating, clean composition. No filesystem conventions to learn.
Multi-page apps without the wiring
Declare a route dict that maps paths to component instances. The router handles the rest: matching, 404 fallback, URL kwargs. Add AuthRouterComponent to gate routes behind Flask sessions.
from weaverlet import WeaverletApp, SimpleRouterComponent
router = SimpleRouterComponent(
routes={
"/": HomePage(),
"/about": AboutPage(),
"/docs": DocsPage(),
},
not_found_page_component=NotFoundPage(),
)
WeaverletApp(root_component=router).app.run()
Class-based encapsulation, server-side
Every UI block is a WeaverletComponent that owns its layout, callbacks, and IDs. Instantiate it twice and Dash treats the copies as independent. Same React mental model, pure Python.
from weaverlet import WeaverletComponent, WeaverletApp, Identifier
from dash_extensions.enrich import Input, Output
from dash import html, dcc
class EchoComponent(WeaverletComponent):
text_id = Identifier()
echo_id = Identifier()
def get_layout(self):
return html.Div([
dcc.Input(id=self.text_id),
html.Div(id=self.echo_id),
])
def register_callbacks(self, app):
@app.callback(Output(self.echo_id, "children"),
Input(self.text_id, "value"))
def echo(v): return v or ""
Auto-unique IDs that never collide
The Identifier() descriptor generates a unique Dash ID per instance, computed lazily and cached. You stop managing string IDs by hand and stop hitting DuplicateIdError.
class MyButton(WeaverletComponent):
btn_id = Identifier()
def get_layout(self):
return html.Button("Click me", id=self.btn_id)
# Two instances, two unique Dash IDs, zero collisions.
btn_a = MyButton()
btn_b = MyButton()
Signal-based events, not dcc.Store plumbing
SignalComponent plus typed adapters (SignalInput, SignalOutput, SignalTrigger) replace ad-hoc dcc.Store wiring as the cross-component event bus. Multiple producers and consumers work transparently thanks to DashProxy under the hood.
from weaverlet import SignalComponent, SignalOutput, SignalInput
# Producer: emit a payload dict to the signal.
@app.callback(SignalOutput(self.sig), Input(self.btn, "n_clicks"))
def emit(n):
return {"clicks": n}
# Consumer: any component, anywhere in the DAG.
@app.callback(Output(self.label, "children"), SignalInput(self.sig))
def show(payload):
return f"Got: {payload}"
Auth routing with Flask sessions
AuthRouterComponent gates routes behind flask.session. Unauthenticated visitors are redirected to the login route; on success, your login callback sets a session key and the user is bounced back to the page they were trying to reach.
from weaverlet import AuthRouterComponent
router = AuthRouterComponent(
routes={
"/": {"component": Dashboard(), "login_required": True},
"/login": {"component": LoginPage(), "login_required": False},
},
not_found_page_component=NotFound(),
)
Built for LLM coding assistants
Weaverlet ships a ReadMe.LLM.md following the ReadMe.LLM methodology plus an llms.txt at the repo root following the llmstxt.org convention. Drop a CDN URL into Claude, GPT, Codex, or Cursor and your assistant writes idiomatic Weaverlet code without guessing the API. See the full breakdown.
# Latest (tracks main):
https://cdn.jsdelivr.net/gh/observatoriogeo/weaverlet@main/ReadMe.LLM.md
# Pinned to v0.3.0:
https://cdn.jsdelivr.net/gh/observatoriogeo/weaverlet@v0.3.0/ReadMe.LLM.md
Get started
pip install weaverlet
