Skip to main content
Version: Next

WeaverletApp lifecycle

WeaverletApp is the orchestrator. You hand it a root component and it produces a regular Dash app, ready to run.

from weaverlet import WeaverletApp, WeaverletComponent
from dash import html

class Root(WeaverletComponent):
def get_layout(self):
return html.Div("Hello")

wapp = WeaverletApp(
root_component=Root(),
context={"environment": "dev"},
title="My App",
)
wapp.app.run(port=8050)

The 4-step lifecycle

When you call WeaverletApp(root_component=root), it does the following, in order:

1. Construct the underlying Dash app

WeaverletApp.app is a dash_extensions.enrich.DashProxy, not a plain dash.Dash. The proxy gives you:

  • group= multi-output callbacks (multiple callbacks can write to the same Output).
  • Serverside(...) return-value wrappers for server-side caching.
  • dash_extensions.javascript.assign() for client-side JS hooks.
  • All standard Dash features unchanged.

Extra kwargs to WeaverletApp(...) flow through to the proxy. title, external_stylesheets, suppress_callback_exceptions, prevent_initial_callbacks, and any other Dash kwarg via **dash_kwargs.

2. Walk the component DAG

Starting from root_component, Weaverlet walks the parent → child graph by introspecting each component's __dict__ for attributes that are themselves WeaverletComponent instances (plus the ComponentsDict / ComponentsList / ComponentsOrderedDict collection types for routers and similar).

The walk:

  • Detects cycles via a visited set; cycles must be broken explicitly with DetachedComponentRef.
  • Propagates the context dictionary so every component can call self.get_context().
  • Runs each component's initialize() hook (if defined).

3. Register every callback

After the walk, WeaverletApp calls register_callbacks(self.app) on every component in the DAG. Each component registers only the callbacks it owns; the framework guarantees the order is deterministic.

4. Build the layout

Finally, WeaverletApp calls root_component.get_layout() and assigns the result as the Dash app's layout. From this point on, the app is ready to serve.

Running the app

Local development

wapp.app.run(port=8050, debug=True)
app.run_server is removed in Dash 4

Use wapp.app.run(...): wapp.app.run_server(...) no longer exists. If you're porting code from a Dash 2.x / pre-0.3 Weaverlet codebase, this is the most common breaking change.

Jupyter

Dash 4 absorbed Jupyter support into core. Pass jupyter_mode to app.run:

wapp.app.run(jupyter_mode="inline") # or "tab" or "external"

No extras needed. Don't pass jupyter_mode=True to WeaverletApp(...): it was removed in 0.3.1 (it raises TypeError with a migration message).

Production (WSGI)

# app.py
from weaverlet import WeaverletApp, WeaverletComponent
from dash import html

class Root(WeaverletComponent):
def get_layout(self):
return html.Div("Hello")

application = WeaverletApp(root_component=Root()).app.server

Then run with gunicorn:

gunicorn app:application -w 2 -b 0.0.0.0:8080

The assets_folder auto-resolution

WeaverletApp auto-resolves assets_folder to dirname(sys.modules['__main__'].__file__) + "/assets". This matters for libraries that write JS files to assets/ at runtime. Most notably dash_extensions.javascript.assign(), which writes to a CWD-relative path by default.

If you run your app from an IDE, uv run, or a test runner where the CWD differs from the script directory, this auto-resolution prevents the "No match for function0" errors you'd otherwise see from dash-leaflet style handlers and similar libraries.

Override explicitly if you need to:

wapp = WeaverletApp(root_component=root, assets_folder="/path/to/assets")

Attributes after construction

AttributeTypeWhat it is
wapp.appDashProxyThe underlying Dash app. Use wapp.app.run(...), wapp.app.server, etc.
wapp.root_componentWeaverletComponentThe root you passed in.
wapp.rootWeaverletComponentAlias for root_component.
wapp.contextdictThe context dict (defaults to {} if not passed).